{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## Datawhale组队学习Pandas\n",
    "## 第十章 时序数据\n",
    "## 第十次打卡"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "![](https://ai-studio-static-online.cdn.bcebos.com/790c6e78e7664d75878b102266377ef8799688f2cc4d4a559b82d2767b9938fc)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "!pip install pandas==1.2.0\r\n",
    "!unzip data/data65130/data.zip -d data/ > /dev/null 2>&1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "这表格总结的贼棒\n",
    "## 一、时序中的基本对象\n",
    "\n",
    "时间序列的概念在日常生活中十分常见，但对于一个具体的时序事件而言，可以从多个时间对象的角度来描述。例如2020年9月7日周一早上8点整需要到教室上课，这个课会在当天早上10点结束，其中包含了哪些时间概念？\n",
    "\n",
    "* 第一，会出现时间戳（Date times）的概念，即'2020-9-7 08:00:00'和'2020-9-7 10:00:00'这两个时间点分别代表了上课和下课的时刻，在`pandas`中称为`Timestamp`。同时，一系列的时间戳可以组成`DatetimeIndex`，而将它放到`Series`中后，`Series`的类型就变为了`datetime64[ns]`，如果有涉及时区则为`datetime64[ns, tz]`，其中tz是timezone的简写。\n",
    "\n",
    "* 第二，会出现时间差（Time deltas）的概念，即上课需要的时间，两个`Timestamp`做差就得到了时间差，pandas中利用`Timedelta`来表示。类似的，一系列的时间差就组成了`TimedeltaIndex`， 而将它放到`Series`中后，`Series`的类型就变为了`timedelta64[ns]`。\n",
    "\n",
    "* 第三，会出现时间段（Time spans）的概念，即在8点到10点这个区间都会持续地在上课，在`pandas`利用`Period`来表示。类似的，一系列的时间段就组成了`PeriodIndex`， 而将它放到`Series`中后，`Series`的类型就变为了`Period`。\n",
    "\n",
    "* 第四，会出现日期偏置（Date offsets）的概念，假设你只知道9月的第一个周一早上8点要去上课，但不知道具体的日期，那么就需要一个类型来处理此类需求。再例如，想要知道2020年9月7日后的第30个工作日是哪一天，那么时间差就解决不了你的问题，从而`pandas`中的`DateOffset`就出现了。同时，`pandas`中没有为一列时间偏置专门设计存储类型，理由也很简单，因为需求比较奇怪，一般来说我们只需要对一批时间特征做一个统一的特殊日期偏置。\n",
    "\n",
    "通过这个简单的例子，就能够容易地总结出官方文档中的这个[表格](https://pandas.pydata.org/docs/user_guide/timeseries.html#overview)：\n",
    "\n",
    "|概念 |                          单元素类型              |    数组类型         |                pandas数据类型|\n",
    "|:---------|:----------|:-----------|:------------|\n",
    "|Date times           |           `Timestamp`       |       `DatetimeIndex`  |   `datetime64[ns]`|\n",
    "|Time deltas          |           `Timedelta`        |      `TimedeltaIndex` |  `timedelta64[ns]`|\n",
    "|Time spans            |          `Period`           |      `PeriodIndex`   |    `period[freq]`|\n",
    "|Date offsets          |          `DateOffset`         |    `None`          |    `None`|\n",
    "\n",
    "由于时间段对象`Period/PeriodIndex`的使用频率并不高，因此将不进行讲解，而只涉及时间戳序列、时间差序列和日期偏置的相关内容。\n",
    "\n",
    "## 二、时间戳\n",
    "### 1. Timestamp的构造与属性\n",
    "\n",
    "单个时间戳的生成利用`pd.Timestamp`实现，一般而言的常见日期格式都能被成功地转换：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-01-01 00:00:00')"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts = pd.Timestamp('2020/1/1')\n",
    "ts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-01-01 08:10:30')"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts = pd.Timestamp('2020-1-1 08:10:30')\n",
    "ts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "通过`year, month, day, hour, min, second`可以获取具体的数值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.year"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.month"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.day"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.hour"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.minute"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts.second"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "在`pandas`中，时间戳的最小精度为纳秒`ns`，由于使用了64位存储，可以表示的时间范围大约可以如下计算：\n",
    "$$\\rm Time\\,Range = \\frac{2^{64}}{10^9\\times 60\\times 60\\times 24\\times 365} \\approx 585 (Years)$$\n",
    "通过`pd.Timestamp.max`和`pd.Timestamp.min`可以获取时间戳表示的范围，可以看到确实表示的区间年数大小正如上述计算结果："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2262-04-11 23:47:16.854775807')"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp.max"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('1677-09-21 00:12:43.145225')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp.min"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "585"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp.max.year - pd.Timestamp.min.year"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "**那到了2262年会怎么样**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "pd.Timestamp?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 2. Datetime序列的生成\n",
    "\n",
    "一组时间戳可以组成时间序列，可以用`to_datetime`和`date_range`来生成。其中，`to_datetime`能够把一列时间戳格式的对象转换成为`datetime64[ns]`类型的时间序列："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2019-10-05\n",
       "1   2019-09-04\n",
       "2   2019-09-12\n",
       "3   2020-01-03\n",
       "4   2019-11-06\n",
       "Name: Test_Date, dtype: datetime64[ns]"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.to_datetime(['2020-1-1', '2020-1-3', '2020-1-6'])\n",
    "df = pd.read_csv('data/learn_pandas.csv')\n",
    "s = pd.to_datetime(df.Test_Date)\n",
    "s.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "在极少数情况，时间戳的格式不满足转换时，可以强制使用`format`进行匹配："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-01-03'], dtype='datetime64[ns]', freq=None)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "temp = pd.to_datetime(['2020\\\\1\\\\1','2020\\\\1\\\\3'],format='%Y\\\\%m\\\\%d')\n",
    "temp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "注意上面由于传入的是列表，而非`pandas`内部的`Series`，因此返回的是`DatetimeIndex`，如果想要转为`datetime64[ns]`的序列，需要显式用`Series`转化："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01\n",
       "1   2020-01-03\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Series(temp).head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "另外，还存在一种把表的多列时间属性拼接转为时间序列的`to_datetime`操作，此时的列名必须和以下给定的时间关键词列名一致："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01 10:30:20\n",
       "1   2020-01-02 20:50:40\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_date_cols = pd.DataFrame({'year': [2020, 2020],\n",
    "                             'month': [1, 1],\n",
    "                             'day': [1, 2],\n",
    "                             'hour': [10, 20],\n",
    "                             'minute': [30, 50],\n",
    "                             'second': [20, 40]})\n",
    "pd.to_datetime(df_date_cols)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "`date_range`是一种生成连续间隔时间的一种方法，其重要的参数为`start, end, freq, periods`，它们分别表示开始时间，结束时间，时间间隔，时间戳个数。其中，四个中的三个参数决定了，那么剩下的一个就随之确定了。这里要注意，开始或结束日期如果作为端点则它会被包含："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21'], dtype='datetime64[ns]', freq='10D')"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('2020-1-1','2020-1-21', freq='10D') # 包含"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',\n",
       "               '2020-02-10', '2020-02-20'],\n",
       "              dtype='datetime64[ns]', freq='10D')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('2020-1-1','2020-2-28', freq='10D')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01 00:00:00', '2020-01-12 14:24:00',\n",
       "               '2020-01-24 04:48:00', '2020-02-04 19:12:00',\n",
       "               '2020-02-16 09:36:00', '2020-02-28 00:00:00'],\n",
       "              dtype='datetime64[ns]', freq=None)"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('2020-1-1', '2020-2-28', periods=6) # 由于结束日期无法取到，freq不为10天"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "pd.date_range?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "`freq`可以取的值：Y，M，D，H，M，"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "#### 【练一练】\n",
    "`Timestamp`上定义了一个`value`属性，其返回的整数值代表了从1970年1月1日零点到给定时间戳相差的纳秒数，请利用这个属性构造一个随机生成给定日期区间内日期序列的函数。\n",
    "\n",
    "首先看看value的效果，返回一个很大的数字\n",
    "\n",
    "思路是把给定的日期区间转换成这个很大的数字，然后在这两个很大的数字区间内部随机取一个数字，最后再转换回日期序列\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "345340800000000000"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp(year=1980,month=12,day=11).value"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import random\r\n",
    "def rantime(y1,m1,d1,y2,m2,d2):\r\n",
    "    \r\n",
    "    t1 = pd.Timestamp(year=y1,month=m1,day=d1)\r\n",
    "    t2 = pd.Timestamp(year=y2,month=m2,day=d2)\r\n",
    "    t3 = random.randint(t1,t2)\r\n",
    "    return t3 \r\n",
    "\r\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "这里的`freq`参数与`DateOffset`对象紧密相关，将在第四节介绍其具体的用法。\n",
    "\n",
    "#### 【END】\n",
    "最后，要介绍一种改变序列采样频率的方法`asfreq`，它能够根据给定的`freq`对序列进行类似于`reindex`的操作："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01    0.217961\n",
       "2020-01-03    0.302261\n",
       "2020-01-05    0.688799\n",
       "2020-01-07    0.366520\n",
       "2020-01-09    0.336918\n",
       "dtype: float64"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = pd.Series(np.random.rand(5), index=pd.to_datetime(['2020-1-%d'%i for i in range(1,10,2)]))\n",
    "s.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01    0.676040\n",
       "2020-01-02         NaN\n",
       "2020-01-03    0.428474\n",
       "2020-01-04         NaN\n",
       "2020-01-05    0.535679\n",
       "Freq: D, dtype: float64"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.asfreq('D').head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01 00:00:00    0.217961\n",
       "2020-01-01 12:00:00         NaN\n",
       "2020-01-02 00:00:00         NaN\n",
       "2020-01-02 12:00:00         NaN\n",
       "2020-01-03 00:00:00    0.302261\n",
       "Freq: 12H, dtype: float64"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.asfreq('12H').head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "#### 【练一练】\n",
    "前面提到了`datetime64[ns]`本质上可以理解为一个大整数，对于一个该类型的序列，可以使用`max, min, mean`，来取得最大时间戳、最小时间戳和“平均”时间戳。\n",
    "#### 【END】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<bound method DatetimeIndexOpsMixin.min of DatetimeIndex(['2020-01-01', '2020-01-11', '2020-01-21', '2020-01-31',\n",
       "               '2020-02-10', '2020-02-20'],\n",
       "              dtype='datetime64[ns]', freq='10D')>"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('2020-1-1','2020-2-28', freq='10D').max\r\n",
    "pd.date_range('2020-1-1','2020-2-28', freq='10D').min"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "\n",
    "### 3. dt对象\n",
    "\n",
    "如同`category, string`的序列上定义了`cat, str`来完成分类数据和文本数据的操作，在时序类型的序列上定义了`dt`对象来完成许多时间序列的相关操作。这里对于`datetime64[ns]`类型而言，可以大致分为三类操作：取出时间相关的属性、判断时间戳是否满足条件、取整操作。\n",
    "\n",
    "第一类操作的常用属性包括：`date, time, year, month, day, hour, minute, second, microsecond, nanosecond, dayofweek, dayofyear, weekofyear, daysinmonth, quarter`，其中`daysinmonth, quarter`分别表示月中的第几天和季度。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    2020-01-01\n",
       "1    2020-01-02\n",
       "2    2020-01-03\n",
       "dtype: object"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = pd.Series(pd.date_range('2020-1-1','2020-1-3', freq='D'))\n",
    "s.dt.date"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    00:00:00\n",
       "1    00:00:00\n",
       "2    00:00:00\n",
       "dtype: object"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    1\n",
       "1    2\n",
       "2    3\n",
       "dtype: int64"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.dayofyear"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    31\n",
       "1    31\n",
       "2    31\n",
       "dtype: int64"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.daysinmonth"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "在这些属性中，经常使用的是`dayofweek`，它返回了周中的星期情况，周一为0、周二为1，以此类推："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    2\n",
       "1    3\n",
       "2    4\n",
       "dtype: int64"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.dayofweek"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "此外，可以通过`month_name, day_name`返回英文的月名和星期名，注意它们是方法而不是属性："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    January\n",
       "1    January\n",
       "2    January\n",
       "dtype: object"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.month_name()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    Wednesday\n",
       "1     Thursday\n",
       "2       Friday\n",
       "dtype: object"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.day_name()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "|方法/属性|干啥的|\n",
    "|--|--|\n",
    "date|日期\n",
    "time|时间\n",
    "year| 年份\n",
    "month|月份\n",
    "day|第几天\n",
    "hour|几点钟\n",
    "minute|几分钟\n",
    "second|几秒钟\n",
    "microsecond|几微秒\n",
    "nanosecond|几纳秒\n",
    "dayofweek|这周的第几天\n",
    "dayofyear|这年的第几天\n",
    "weekofyear|这年的第几周\n",
    "daysinmonth|这个月的第几天\n",
    "quarter|季度\n",
    "month_name|月份名称\n",
    "day_name|星期几"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "第二类判断操作主要用于测试是否为月/季/年的第一天或者最后一天："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0     True\n",
       "1    False\n",
       "2    False\n",
       "dtype: bool"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.is_year_start # 还可选 is_quarter/month_start"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    False\n",
       "1    False\n",
       "2    False\n",
       "dtype: bool"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.is_year_end # 还可选 is_quarter/month_end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "第三类的取整操作包含`round, ceil, floor`，它们的公共参数为`freq`，常用的包括`H, min, S`（小时、分钟、秒），所有可选的`freq`可参考[此处](https://pandas.pydata.org/docs/user_guide/timeseries.html#offset-aliases>)。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01 20:31:00\n",
       "1   2020-01-01 21:16:00\n",
       "2   2020-01-01 22:01:00\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = pd.Series(pd.date_range('2020-1-1 20:31:00', '2020-1-1 22:30:00', freq='45min'))\n",
    "s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01 21:00:00\n",
       "1   2020-01-01 21:00:00\n",
       "2   2020-01-01 22:00:00\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.round('1H')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01 21:00:00\n",
       "1   2020-01-01 22:00:00\n",
       "2   2020-01-01 23:00:00\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.ceil('1H')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01 20:00:00\n",
       "1   2020-01-01 21:00:00\n",
       "2   2020-01-01 22:00:00\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.floor('1H')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "操作|含义|\n",
    "|--|--|\n",
    "round|以30分为界，30和30以下的往下\n",
    "ceil|往上约\n",
    "floor|往下约"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 4. 时间戳的切片与索引\n",
    "\n",
    "一般而言，时间戳序列作为索引使用。如果想要选出某个子时间戳序列，第一类方法是利用`dt`对象和布尔条件联合使用，另一种方式是利用切片，后者常用于连续时间戳。下面，举一些例子说明："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01    1\n",
       "2020-01-02    1\n",
       "2020-01-03    0\n",
       "2020-01-04    0\n",
       "2020-01-05    1\n",
       "Freq: D, dtype: int64"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01','2020-12-31'))\n",
    "idx = pd.Series(s.index).dt\n",
    "s.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Example1：每月的第一天或者最后一天"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01    0\n",
       "2020-01-31    0\n",
       "2020-02-01    0\n",
       "2020-02-29    0\n",
       "2020-03-01    1\n",
       "dtype: int64"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s[(idx.is_month_start|idx.is_month_end).values].head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Example2：双休日"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-04    0\n",
       "2020-01-05    0\n",
       "2020-01-11    0\n",
       "2020-01-12    0\n",
       "2020-01-18    1\n",
       "dtype: int64"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s[idx.dayofweek.isin([5,6]).values].head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Example3：取出单日值"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s['2020-01-01']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s['20200101'] # 自动转换标准格式"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Example4：取出七月"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-07-01    1\n",
       "2020-07-02    0\n",
       "2020-07-03    1\n",
       "2020-07-04    1\n",
       "2020-07-05    0\n",
       "Freq: D, dtype: int64"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s['2020-07'].head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "Example5：取出5月初至7月15日"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-05-01    0\n",
       "2020-05-02    1\n",
       "2020-05-03    1\n",
       "2020-05-04    1\n",
       "2020-05-05    0\n",
       "Freq: D, dtype: int64"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s['2020-05':'2020-7-15'].head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-07-11    0\n",
       "2020-07-12    0\n",
       "2020-07-13    0\n",
       "2020-07-14    1\n",
       "2020-07-15    1\n",
       "Freq: D, dtype: int64"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s['2020-05':'2020-7-15'].tail()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 三、时间差\n",
    "### 1. Timedelta的生成\n",
    "\n",
    "正如在第一节中所说，时间差可以理解为两个时间戳的差，这里也可以通过`pd.Timedelta`来构造："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timedelta('1 days 00:25:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp('20200102 08:00:00')-pd.Timestamp('20200101 07:35:00')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timedelta('1 days 00:25:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timedelta(days=1, minutes=25) # 需要注意加s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timedelta('1 days 00:25:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timedelta('1 days 25 minutes') # 字符串生成"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "`pd.Timedelta?`可以使用的功能：\n",
    "```\n",
    "Parameters\n",
    "----------\n",
    "value : Timedelta, timedelta, np.timedelta64, str, or int\n",
    "unit : str, default 'ns'\n",
    "    Denote the unit of the input, if input is an integer.\n",
    "\n",
    "    Possible values:\n",
    "直接赋值：\n",
    "    * 'W', 'D', 'T', 'S', 'L', 'U', or 'N'\n",
    "    * 'days' or 'day'\n",
    "    * 'hours', 'hour', 'hr', or 'h'\n",
    "    * 'minutes', 'minute', 'min', or 'm'\n",
    "    * 'seconds', 'second', or 'sec'\n",
    "    * 'milliseconds', 'millisecond', 'millis', or 'milli'\n",
    "    * 'microseconds', 'microsecond', 'micros', or 'micro'\n",
    "    * 'nanoseconds', 'nanosecond', 'nanos', 'nano', or 'ns'.\n",
    "\n",
    "**kwargs\n",
    "    Available kwargs: {days, seconds, microseconds,\n",
    "    milliseconds, minutes, hours, weeks}.\n",
    "    Values for construction in compat with datetime.timedelta.\n",
    "    Numpy ints and floats will be coerced to python ints and floats.\n",
    " ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "生成时间差序列的主要方式是`pd.to_timedelta`，其类型为`timedelta64[ns]`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   0 days 00:04:34\n",
       "1   0 days 00:04:20\n",
       "2   0 days 00:05:22\n",
       "3   0 days 00:04:08\n",
       "4   0 days 00:05:22\n",
       "Name: Time_Record, dtype: timedelta64[ns]"
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = pd.to_timedelta(df.Time_Record)\n",
    "s.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "与`date_range`一样，时间差序列也可以用`timedelta_range`来生成，它们两者具有一致的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TimedeltaIndex(['0 days 00:00:00', '0 days 00:06:00', '0 days 00:12:00'], dtype='timedelta64[ns]', freq='6T')"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.timedelta_range('0s', '1000s', freq='6min')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TimedeltaIndex(['0 days 00:00:00', '0 days 00:08:20', '0 days 00:16:40'], dtype='timedelta64[ns]', freq=None)"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.timedelta_range('0s', '1000s', periods=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "对于`Timedelta`序列，同样也定义了`dt`对象，上面主要定义了的属性包括`days, seconds, mircroseconds, nanoseconds`，它们分别返回了对应的时间差特征。需要注意的是，这里的`seconds`不是指单纯的秒，而是对天数取余后剩余的秒数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    274\n",
       "1    260\n",
       "2    322\n",
       "3    248\n",
       "4    322\n",
       "Name: Time_Record, dtype: int64"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.seconds.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "如果不想对天数取余而直接对应秒数，可以使用`total_seconds`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    274.0\n",
       "1    260.0\n",
       "2    322.0\n",
       "3    248.0\n",
       "4    322.0\n",
       "Name: Time_Record, dtype: float64"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.dt.total_seconds().head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "与时间戳序列类似，取整函数也是可以在`dt`对象上使用的："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   0 days 00:05:00\n",
       "1   0 days 00:04:00\n",
       "2   0 days 00:05:00\n",
       "3   0 days 00:04:00\n",
       "4   0 days 00:05:00\n",
       "Name: Time_Record, dtype: timedelta64[ns]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.to_timedelta(df.Time_Record).dt.round('min').head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 2. Timedelta的运算\n",
    "\n",
    "时间差支持的常用运算有三类：与标量的乘法运算、与时间戳的加减法运算、与时间差的加减法与除法运算："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timedelta('2 days 00:00:00')"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td1 = pd.Timedelta(days=1)\n",
    "td2 = pd.Timedelta(days=3)\n",
    "ts = pd.Timestamp('20200101')\n",
    "td1 * 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timedelta('2 days 00:00:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td2 - td1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-01-02 00:00:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts + td1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2019-12-31 00:00:00')"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ts - td1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "这些运算都可以移植到时间差的序列上："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TimedeltaIndex(['5 days', '10 days', '15 days', '20 days', '25 days'], dtype='timedelta64[ns]', freq='5D')"
      ]
     },
     "execution_count": 60,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td1 = pd.timedelta_range(start='1 days', periods=5)\n",
    "td2 = pd.timedelta_range(start='12 hours', freq='2H', periods=5)\n",
    "ts = pd.date_range('20200101', '20200105')\n",
    "td1 * 5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0    0 days\n",
       "1    2 days\n",
       "2    6 days\n",
       "3   12 days\n",
       "4   20 days\n",
       "dtype: timedelta64[ns]"
      ]
     },
     "execution_count": 61,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td1 * pd.Series(list(range(5))) # 逐个相乘"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TimedeltaIndex(['0 days 12:00:00', '1 days 10:00:00', '2 days 08:00:00',\n",
       "                '3 days 06:00:00', '4 days 04:00:00'],\n",
       "               dtype='timedelta64[ns]', freq=None)"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td1 - td2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05',\n",
       "               '2020-01-06'],\n",
       "              dtype='datetime64[ns]', freq='D')"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td1 + pd.Timestamp('20200101')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-02', '2020-01-04', '2020-01-06', '2020-01-08',\n",
       "               '2020-01-10'],\n",
       "              dtype='datetime64[ns]', freq=None)"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "td1 + ts # 逐个相加"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 四、日期偏置\n",
    "### 1. Offset对象\n",
    "\n",
    "日期偏置是一种和日历相关的特殊时间差，例如回到第一节中的两个问题：如何求2020年9月第一个周一的日期，以及如何求2020年9月7日后的第30个工作日是哪一天。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-09-07 00:00:00')"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp('20200831') + pd.offsets.WeekOfMonth(week=0,weekday=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-10-19 00:00:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp('20200907') + pd.offsets.BDay(30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "从上面的例子中可以看到，`Offset`对象在`pd.offsets`中被定义。当使用`+`时获取离其最近的下一个日期，当使用`-`时获取离其最近的上一个日期："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-08-03 00:00:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp('20200831') - pd.offsets.WeekOfMonth(week=0,weekday=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-07-27 00:00:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp('20200907') - pd.offsets.BDay(30)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Timestamp('2020-09-30 00:00:00')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.Timestamp('20200907') + pd.offsets.MonthEnd()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "常用的日期偏置如下可以查阅这里的[文档](https://pandas.pydata.org/docs/user_guide/timeseries.html#dateoffset-objects)描述。在文档罗列的`Offset`中，需要介绍一个特殊的`Offset`对象`CDay`，其中的`holidays, weekmask`参数能够分别对自定义的日期和星期进行过滤，前者传入了需要过滤的日期列表，后者传入的是三个字母的星期缩写构成的星期字符串，其作用是只保留字符串中出现的星期："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-08    2\n",
       "2020-01-09    3\n",
       "2020-01-10    4\n",
       "2020-01-11    5\n",
       "Freq: D, dtype: int64"
      ]
     },
     "execution_count": 65,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_filter = pd.offsets.CDay(n=1,weekmask='Wed Fri',holidays=['20200109'])\n",
    "dr = pd.date_range('20200108', '20200111')\n",
    "dr.to_series().dt.dayofweek"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Timestamp('2020-01-10 00:00:00'),\n",
       " Timestamp('2020-01-10 00:00:00'),\n",
       " Timestamp('2020-01-15 00:00:00'),\n",
       " Timestamp('2020-01-15 00:00:00')]"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[i + my_filter for i in dr]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "上面的例子中，`n`表示增加一天`CDay`，`dr`中的第一天为`20200108`，但由于下一天`20200109`被排除了，并且`20200110`是合法的周五，因此转为`20200110`，其他后面的日期处理类似。\n",
    "#### 【CAUTION】不要使用部分`Offset`\n",
    "在当前版本下由于一些 ``bug`` ，不要使用 ``Day`` 级别以下的 ``Offset`` 对象，比如 ``Hour, Second`` 等，请使用对应的 ``Timedelta`` 对象来代替。\n",
    "#### 【END】\n",
    "### 2. 偏置字符串\n",
    "\n",
    "前面提到了关于`date_range`的`freq`取值可用`Offset`对象，同时在`pandas`中几乎每一个`Offset`对象绑定了日期偏置字符串（`frequencies strings/offset aliases`），可以指定`Offset`对应的字符串来替代使用。下面举一些常见的例子。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200331', freq='MS') # 月初"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200331', freq='M') # 月末"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',\n",
       "               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],\n",
       "              dtype='datetime64[ns]', freq='B')"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200110', freq='B') # 工作日"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='W-MON')"
      ]
     },
     "execution_count": 69,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200201', freq='W-MON') # 周一"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')"
      ]
     },
     "execution_count": 70,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200201', freq='WOM-1MON') # 每月第一个周一"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "上面的这些字符串，等价于使用如下的`Offset`对象："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-02-01', '2020-03-01'], dtype='datetime64[ns]', freq='MS')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200331', freq=pd.offsets.MonthBegin())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-31', '2020-02-29', '2020-03-31'], dtype='datetime64[ns]', freq='M')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200331', freq=pd.offsets.MonthEnd())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-06',\n",
       "               '2020-01-07', '2020-01-08', '2020-01-09', '2020-01-10'],\n",
       "              dtype='datetime64[ns]', freq='B')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200110', freq=pd.offsets.BDay())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-06', '2020-01-13', '2020-01-20', '2020-01-27'], dtype='datetime64[ns]', freq='C')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200201', freq=pd.offsets.CDay(weekmask='Mon'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "DatetimeIndex(['2020-01-06'], dtype='datetime64[ns]', freq='WOM-1MON')"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.date_range('20200101','20200201', freq=pd.offsets.WeekOfMonth(week=0,weekday=0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "含义|freq|offset\n",
    "|--|--|--|\n",
    "月初|'MS'|pd.offsets.MonthBegin()\n",
    "月末|'M' |pd.offsets.MonthEnd()\n",
    "工作日|'B' |pd.offsets.BDay()\n",
    "周一|'W-MON'|pd.offsets.CDay(weekmask='Mon')\n",
    "每月第一个周一|'WOM-1MON'|pd.offsets.WeekOfMonth(week=0,weekday=0)\n",
    " \n",
    " \n",
    " \n",
    " "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "#### 【CAUTION】关于时区问题的说明\n",
    "各类时间对象的开发，除了使用`python`内置的`datetime`模块，`pandas`还利用了`dateutil`模块，很大一部分是为了处理时区问题。总所周知，我国是没有夏令时调整时间一说的，但有些国家会有这种做法，导致了相对而言一天里可能会有23/24/25个小时，也就是`relativedelta`，这使得`Offset`对象和`Timedelta`对象有了对同一问题处理产生不同结果的现象，其中的规则也较为复杂，官方文档的写法存在部分描述错误，并且难以对描述做出统一修正，因为牵涉到了`Offset`相关的很多组件。因此，本教程完全不考虑时区处理，如果对时区处理的时间偏置有兴趣了解讨论，可以联系我或者参见[这里](https://github.com/pandas-dev/pandas/pull/36516)的讨论。\n",
    "#### 【END】\n",
    "## 五、时序中的滑窗与分组\n",
    "### 1. 滑动窗口\n",
    "\n",
    "所谓时序的滑窗函数，即把滑动窗口用`freq`关键词代替，下面给出一个具体的应用案例：在股票市场中有一个指标为`BOLL`指标，它由中轨线、上轨线、下轨线这三根线构成，具体的计算方法分别是`N`日均值线、`N`日均值加两倍`N`日标准差线、`N`日均值减两倍`N`日标准差线。利用`rolling`对象计算`N=30`的`BOLL`指标可以如下写出："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01   -1\n",
       "2020-01-02   -2\n",
       "2020-01-03   -1\n",
       "2020-01-06   -1\n",
       "2020-01-07   -2\n",
       "Freq: B, dtype: int64"
      ]
     },
     "execution_count": 71,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "idx = pd.date_range('20200101', '20201231', freq='B')\n",
    "np.random.seed(2020)\n",
    "data = np.random.randint(-1,2,len(idx)).cumsum() # 随机游动构造模拟序列\n",
    "s = pd.Series(data,index=idx)\n",
    "s.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x7fbb5fd48e90>]"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXkAAAEICAYAAAC6fYRZAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAIABJREFUeJzs3Xd4VMX6wPHvbHbTe68QEnovAekggiiCiiKo1+5VwV6uv2sv1y4WsCFibzQrCAgI0muA0HtIsimk991kk935/XGSkJC2CaEY5vM8eSCnzony7snMO+8IKSWKoihK66Q73w1QFEVRzh4V5BVFUVoxFeQVRVFaMRXkFUVRWjEV5BVFUVoxFeQVRVFaMRXkFUVRWjEV5JULhhAiQQhhFkIUCSFyhRBLhBARpx0zWAixWghRKITIF0IsFkJ0rbZ/pBAiuZ7rfy2EeNWOdkQKIaQQQl/HvpeEEN9X+14KIfYKIXTVtr0qhPj6tGsVnfY1pWJ/uBDiZyFEVsXz7BNC3NH4T0tR7KOCvHKhmSCldAdCgHTgw8odQohBwArgdyAUaAfsBjYKIaLOQ1srhQI3NnKMt5TSvdrX/Irt3wFGoC3gB9yK9tyK0iJUkFcuSFLKEuAnoGu1zW8D30opZ0opC6WUOVLK54AtwEvnoZnV2/VyXW/+dugPfC2lLJZSlkspd0kpl7Vw+5SLmAryygVJCOEKTEEL4JXfDwYW1nH4AmDMuWtdLb8ABcAdzTh3C/CxEOJGIUSbFm2VoqCCvHLh+U0IkQfkowXu6RXbfdH+f02r45w0wP/cNK9OEngeeF4I4VjPMVlCiLxqX10qtt8ArK84/4QQIk4I0f8ctFm5SKggr1xorpVSegPOwIPAWiFEMJAL2ND66k8XAmSduybWJqVcCiQD99VziL+U0rva18GK83KllE9JKbsBQUAc2gedODctV1o7FeSVC5KU0iql/AWwAkOllMXAZrQ339NNBlady/bV41ngGcC1OSdLKbOAd9AGcn1bsF3KRaw5A0WKctZVvMleDfgABys2PwUsF0IcAr5C+//3CWAQ2gBm9fOdT7tkacWfDqfts0kpLfU0w+m0wdT6jgNASrlGCLEPuB1Y3NCx1dr5FlqGzSHABZgGHJNSZttzvqI0Rr3JKxeaxUKIIrSBzNeA26WU+wGklBuAscB1aP3wiUAftDf9o9WuEQaYT/uKrtj31GnbVzfQlqLTjh1lR/ufo+638LzT8uQfr9juCvwK5AHxaKmUV9txH0Wxi1CLhiiKorRe6k1eURSlFVNBXlEUpRVTQV5RFKUVU0FeURSlFTsvKZT+/v4yMjLyfNxaURTlH2vHjh1ZUsqAppxzXoJ8ZGQksbGx5+PWiqIo/1hCiMSmnqO6axRFUVoxFeQVRVFaMRXkFUVRWjEV5BVFUVoxFeQVRVFaMRXkFUVRWjEV5BVFUVoxFeQVRVGaIfZkLKuSVnGhV/JVi4YoiqLYySZtxOfFs+3kNt7Y9gYAd3a7k8djHm/kzPNHBXlFURQ7vLfjPX4+8jMFlgIA2nu3J9Q9lO8OfMf1Ha+nrWfb89zCuqkgryiK0ogT+Sf4at9XDAkdwhXtrqBPYB/aeLQhuySb8b+O55kNz/D12K8xOBjOd1NrUX3yiqIojfjt2G84CAdeGfIK17a/lraebRFC4O/iz8uDX2ZP5h7e3/n++W5mnVSQVxRFaUC5rZzFxxczLGwYAa61C0COjRzLzZ1v5rsD3/FX4l/noYUNU0FeURSlAZtSN5FpzuTaDtfWe8wTMU/Q3a87j615jOc3Po9N2s5hCxumgryiKEo9pJR8s/8b/Jz9GB4+vN7jHB0cmTV6Fjd3vpnfjv3G7D2zAbDarCQVJJFTklN1vXOdcqkGXhVFUeqxMXUj205u46kBT2HQNTyo6u3szVMDnqKorIhP4j5h2YllpBSmYLFZMOgM9A7sTUJ+AjMunUHPgJ7n6AlUkFcURalTXkker299nbaebbmh4w12nSOE4IVBL+BucCfDlMGI8BFEeUVxMOcg+7P3ExMcg6OD41lueU0qyCuKolQjpeT347/zXux7FFoKmT1mdpMCs5ODE09f8nSNbROZ2NLNtJsK8oqiKNW8vPllfj76M30C+/DcwOfo6NPxfDfpjKggryiKUsFYYOTXY78ypdMUnrnkGXTin5+b8s9/AkVRlBby3cHv0Akd9/W8r1UEeFBBXlEUBYAccy6/Hv2Vq9pdVWvSU76pjB2JOZgs5ae2mbVtu5JyKbNeOHnxp1PdNYqiKMD0DfMosZZwdbsba+17fEEcqw5lMLFPGO9P6Q3AEwvi+OtgBgBPju3EA5e2P6fttZd6k1cURQH2ZMdhK/NCVxZaa9/RjCIAluxNI99UVrVtcLQffdt4M3+7EZvtwqwrr4K8oigXPSklJ0sPYDW1w5hrqrHPapOk5pkZ2SkAS7mN33enVG3rFeHNbYMiScoxseVE9nlqfcNUkFcU5aKXUpSChTyspkiSc8w19qXlmym3ScZ2C6ZbqCcLYo2cLCihzCqJ8HHliu7BeDjrWRibfJ5a3zC7g7wQIkII8bcQ4oAQYr8Q4pGK7b5CiJVCiKMVf/qcveYqiqK0vF0ZuwCwmiNrvckbK4J+hI8rk2Mi2JdSwIr9J7Vtvi44Gxy4pncoS/emkW8uO7cNt0NT3uTLgSeklF2BgcADQoiuwFPAKillB2BVxfeKoij/GNvSdiCtzthKA6uCeqXKoB/h68I1vUNx1Ot4b8URbZuPKwCTYyIoLbexeHfquW24HewO8lLKNCnlzoq/FwIHgTDgGuCbisO+Aeqvx6koinKBKbPaWHZ0E1ZzW5wNejbHZ/Pd5gQAjmUU8X8/7QEgxMsFb1dHxnYLprBUS6UM9XYBoEeYF52DPXjut318vyXxfDxGvZrVJy+EiAT6AFuBICllWsWuk0BQPefcK4SIFULEZmZmNue2iqIoLW5+3A5KdWm4yw68NKEbAG8sO0RRaTkrDmjdMpNjwnHUa+HyvuFR9I/04daBbau2CSF4fIxW/uDP9Ztg3XRY/iz8fA98PR7m3wp7f4Ly0nP+fKKptY2FEO7AWuA1KeUvQog8KaV3tf25UsoG++VjYmJkbGxssxqsKIrSUqSUDP32RgpsJ/hr8hKC3ALYkZjD9bM28/b1PYlLzmPZ3jR2vXB54xdLjSPul3fomrkMg7AiDC7gFgAewZCbCEUnIag7XP8FBHZuVnuFEDuklDFNOadJk6GEEAbgZ+AHKeUvFZvThRAhUso0IUQIkNGUayqKopwvPx1aQgEHiPG8iyA3bZZr3zY+RAe4MT/WiKujAxG+ro1fKHkHfHctXcutLLMNYMi0WfiHRp7ab7PB4SWw7L9gPbdv803JrhHAF8BBKeV71XYtAm6v+PvtwO8t1zxFUZSzw1Rm4t0d72AtCeGpoXdVbRdCMKV/BDsSc4lLyqsaXK1Xahx8PxFcfYm9ahmPlD1IYplnzWN0OugyAR6Og5BeZ+Fp6teUPvkhwK3AKCFEXMXXOOBNYIwQ4igwuuJ7RVGUC9p3B76j2JpNO26lU5BXjX0T+4Sj1wkKS8sJ93Wp/yIl+fD9deDkBbcvJjA8CqBWhk4V/bldMASa0F0jpdwAiHp2X9YyzVEURTk3lsevxWqO4PZ+I2vtC/BwYlTnQFYcSG/4TX7PAjBlw78XgHcbwsusABhzaubaZxaWcjK/hC4hHugdzu0cVDXjVVGUi06ptZTjBQehpB3jeobUecxNA9oA0D7Qve6LSAmxX2ndL+HaWKizwQF/dyeSc2u+yU/6dBMTPtrA3O3GlnsIO6kgryjKRWd/1n5slBNo6IK7U90dGpd2DmTZI8O4pJ1v3RdJ3g4Z+6HfnTU2R/i61Jg1W1puJTFb+/5oemHLPEATqCCvKMpFZ9vJbQBEundr8LguIZ5oOSd12P4FOHpAj0k1Nkf4uNYI8inV3upPf8M/F1SQVxTlorMhZQOyJJwo3zrnbjYuPxn2/QS9bwYnjxq7InxdSM0robxiIZHKwO7jaqjVV38uqCCvKMpFJb80n72Ze7EUdiSiocyZ+uQmwPxbtD75wQ/W2h3h44rVJknLLwFO1b4ZHO1Pcq6Zpk5APVMqyCuKclH56chP2LBRXtS18Rz40xVlwLfXQs4JmPQFeLepdUjl5KnK4G7MMWNwEPRr64O5zEpWkeWMn6EpVJBXFOWiYS4389X+r+jo0R9bSbh9s1krlRTA99dDUTrc8jN0m1jnYeE+2m8Hld00xlwTYd4utPWrGfzPFRXkFUW5aKxPXk9+aT4dXcYDpwKyXX66CzIOwOTvqlIm6xLq7YJOQHKOidJyKxuPZdEj3Ju2fm50C/XEeo6XCVQLeSuKctH4M+FP/Jz9kKYofN0ycasnfbKW5B1wbCWMeQU6jG7wUIODjl6iEOuO7WzIPEpk4jFu7tKZdo7tWPLwsBZ4iqZRQV5RlIvC+uT1rEpaxc2db2bf3lIimvIWv3UWOHlCzJ2NHlpy+DCv/PYqDjZt9usbABvh2FvuhH/4AW6DBjXvAZpJddcoitLqxefF83/r/o8O3h14qM9DGHNMhNvbH1+QCvt/hT631EqXrEvm+zMod3Tm5ZH38+Sw+9nyyOtEfPE5+oAA0p57Hpv53ObKqyCvKEqrll+az4OrH8TJwYkPR32Ik4MLKXlm+zNrtn8ONisMuLfRQ8tSUihas4bkSyewxTuKfX5RjJ4yFvchQwh+6SVt//r1Z/hETaO6axRFadV+OPgDyYXJfHvlt+ilL+uPZlJmlfblyJcUaEG+y3jwbdfo4Xm//AqA7fJxsDaDwdF+VRk8bpcMIGrpUpyiGr9OS1JBXlGUVktKybITy+gf3J+uvj0Z8tZqMgu1RTva+bs1fLLNBsv+TysnPPSxRu+VO38BWR9/jNvQoUT26ABrM7hxQM08+nMd4EEFeUVRWrGdGTtJKEjg1q638tfBdDILS3nqys70DPNiYDu/+k+UEv58CnbPhUufhbB+jd4rb+FCnLp2IfzDD2jj4sKSh4fSNcSz0fPONhXkFUVpdcpt5Xyw8wMWHV9EiFsI49qN48EfDhDi5cw9w6Jw0NW3NEaFv1+HbbNh0IMw/MlG71eWkUHJvn0EPPYYOhetG6hbqFcjZ50bauBVUZRWxSZtvLjpRb7a/xXtvNrx3sj3KDQ7sO5IJpP6hTce4LfNgXVvQ9/b4PJXob4qlNUUr1sHgPvIkS3wBC1LvckritJqSCl5c9ubLDq+iAd6P8DUXlMB+Gj1UWwSbugX0fAFbFZYNx3aDYfxM+wK8ACFa9agDw3BqWOHM32EFqfe5BVFaTU+3PUhcw/N5Y5ud3Bfz/sAsNkkC2KTGRTlRxu/RtImk7ZotWn63QE6B7vuaSstpXjTZjxGjqy/9vx5pIK8oiitQlxGHHP2zuH6DtfzeL/HqwLutoQcknJMTO4f3vhFDvwGemfoMNbu+5q2bUeaTBdkVw2oIK8oSitRudrTY/0eq/FGvSMxF4AxXYMbvoDNBgcWQfvR4FTPuq51KFqzBuHigusllzS90eeACvKKorQKcRlxRHtF4+VUM6slOdeEn5tjvWu5VjFugaKT9ZYQrouUkqI1a3AbNAidk1Nzmn3WqSCvKA2QUrLo+CKe3fAs8Xnx57s5Sj1s0kZcZhy9A3vX2mfMMdtXp+boStDpocPldt+3PCODspSUc150rCnsDvJCiC+FEBlCiH3Vtr0khEgRQsRVfI07O81UlPNjeux0nt3wLIuOL+L+VfeTaco8301S6rA/az+FlkL6BdWetGTMNdlXcfLEOm3Sk7P9E5gsJxIAcGofbfc551pT3uS/Bq6oY/v7UsreFV9LW6ZZinL+nSw+yY8Hf+S6Dtfx/bjvySnJ4c7ld3Ky+OT5bppymlVJq9ALPcPDh9fYbrVJUvPMja8AVVIAqbsgsmn13i0JCQA4tm3bpPPOJbuDvJRyHZBzFtuiKBeUL/d9iURyb8976RXQi8/GfEa2OZs7/7yTlKKU8908pZq/ElfRLyiG3EI9+1Pz2Z+aT1FpOScLSrRiZI1VnIz7AaQVokc16b6WxESEkxP64EYGdc+jlpgM9aAQ4jYgFnhCSpnbAtdUlPPqi71fMPfQXKZ0mkKYexgAvQN7M+fyOdy78l5u/ONGXhnyCiMjRtZ5fmJBIisTV3Ig+wBJBUlMiJ7A5E6TcdE3YaEKxS5FliISCxOw5PVh5N9rqraP7BTAtBFaN0qDy/wVZWhlDKIvg7aDm3RvS0ICjm3bInQX7vDmmbZsFhAN9AbSgHfrO1AIca8QIlYIEZuZqfo1lQvXt/u/ZcbOGVzZ7kqeHvB0jX3d/bvzw7gfCHYL5qHVD/H53s9rnb/o+CKu+/06Zu6cyaGcQ+h1et6JfYcrf74SY4HxXD3GRWNPxiEAjqdqWTVvXNeDIe39OHKyEGPFYtoNdtf89TKUmeHKt+ye4VqpMshfyM7oTV5KmV75dyHEHOCPBo79DPgMICYm5tyuZKsodjqcc5jpsdMZ03YMrw99HYc6Zj2282rHD+N+4IVNLzBz50yO5R2j2FLMwZyDuBvcic+PZ0DwAF4f9jqBroFIKdl6civ/Wfsf/rPuP8y7at4FOTPynyo2dT8AtpJghIDr+4aTll/CpuPZnMgqQggI9Xau+2Tjdoj7HoY8Av5NK0lgKy3FkpSEx1j7s3HOhzN6kxdChFT7diKwr75jFeWf4Jejv2DQGXhx0IvodfW/Azk6OPLakNe4vsP1LDuxjMTCRPoF9SPYLZh7et7DR5d9RKBrIABCCAaGDOTRvo9yIPsAcZlx5+pxLgr7sw4hrc7Ici9CPJ1x1OuI8HFBStgan0OwpzNO+jpKFNissPQ/4BFiV6XJ01mOHwerFefOnVvgKc4eu9/khRBzgZGAvxAiGXgRGCmE6A1IIAG47yy0UVHOCYvVwpITSxjVZlStCTV1cdA58NLgl3h24LMYdIZGjx/Xbhzvxb7Hm9ve5I1hbxDlFdUSzb7oHSs4iLUkFBBV+fCV3TOxibkMiPSt+8S4HyAtDq7/wq61W09XcvgIAE4dOzWr3edKU7JrbpJShkgpDVLKcCnlF1LKW6WUPaSUPaWUV0sp085mYxXlbFpjXEN+aT7Xtr+2SefZE+ABXA2uvDT4JYyFRiYtmsSs3bOwWC3NaapSIackh4zSeKzFNQdYqw+01jvouuNrCOoO3a9v1r1LDx9GODnh2LZN4wefR6rUsKJUmL1zPl4GfwaFaLMXVx5I56cdRib2CeeK7i2TInd55OX0DerL29vf5pO4T/g76W9+uOoHuz8ozhcpJcfyjhHkFoSn4/ld7chmk7y8eD8nC0rIlFsAia5Ee5uuTJUM8aoW5OsadM1NhJQdMPqlJg+2VjLv24tThw4IB/uqVZ4vKsgrCloQO5K/Dx8Zg4POAZtN8sofB0jKMZFvLmuxIA/g7+LP28Pfpl9gP17d+ipxGXH0D+7fYtdvabkluTz696PszNiJTujo5teNYeHDmNJpCr7O9XSFnEWJOSa+2ZxImLcLFr9N6PRuTBs0giMZJsZ0DQLAQSe4+ZI27EnO49JOAbUvsl9bcLspdWqqK8/OxrxzF/5TL/weahXkFQVILjwJOhPmAm2wdMuJbJJyTAih1T45G8ZHj+fN7W+yLnndBR3kn1r/FPuz9/NkzJMUlhWyOXUzs+Jmsc64jh+v+rHBTKEyaxlLTywl3ZSOQODl5MW4duNwd7S/yuPpjDkmAB4d58r/du2tWByk9uDn6xN71H+R/b9AaF/wiWxWGwqWLwebDY/LL+zMGlBBXlEA2JqsJYbl5flRbrWxMDYZD2c9N/aP4IsNJyiz2jA4tOyEFzeDG/2D+rM+eT1PxDzRotduKRmmDDanbmZar2nc1u02AB7o/QALDi/glS2vsCVtC4NCByGlJN2Ujl6nJ9ucTZmtjKO5R/l096ekFqfWuOY7se9wacSlXBV1FUNCh9SZptoQY64W5FekfYeHowf/6vKvpj1U9nFI260t7dcMJQcOkDH9HZx79MCp04U96AoqyCsKcGpCTZk5iMPphSzdm8YNMeF0CPTAJiEtr6TxVYWaoV9QPz6K+4hCSyEejk3P8DjbViauRCIZG1lzEY1r2l/DnL1zeHLdk3T17crh3MPklNSuetLNrxsvDHqBASEDkFJyNPcoPx/9mRWJK1h6YimXBF/CB6M+wNVg/8/WmGPG0TWVLSfX8UDvB5r+c6vsqunatAH2SulvT0fn6krErE/+EfMdVJBXFOBw7mFsZV5gc+WTNccpLbcxJaYNhaVlgPb2eDaCfA9/rUthf/Z+BoYMbPHrn6kVCSto792eKO+a6Z5ODk58efmX/G/L/yiwFDA8fDhd/bpisVrwcfbBy9ELd0d3+gb2rREIu/l3o5t/N54e8DS/HvuVV7a8wvzD87mz+512t8mYa8I9eDXOzXmLB9j/G4QPAO9G1nutQ/GWLZi2bCHomafR+/s3/d7ngQryykVPSsmJot1YTZEALNmTRudgD7qHeZJcMS1+3ZFMBkf7tfibWzf/bgDsy9p3wQX5DFMGuzJ2cUP0XZgtVlwca3arRHhGMOfyOc26tsHBwOROk1mRuILvD3zPLV1vsTvDKCEvnTK3fdzdZWrT3+IPLob0vXDl9Ca3WUpJ5swP0AcF4T1lSpPPP18u3Ko6inKOxOfHY7bl4SG74FjR7z6lfwRCCEK8nBECZq+L56+DGS1+by8nL9p6tmV3xu4Wv/aZkFLy2Z7PkEi+WuHFo/N3nZX7TOowiQxzBoeyD9l9TmrRCYA6a8c3qCANFj0EIb21hbqbqHj9esy7duE/bdoFuwpUXVSQVy56fxv/BqCta09+njaYz2+L4V+XaEWn9A465t+r5c0fSS88K/e/JPgStp3cdkFNjPpk9yfMPzwff+sYbJZAjqQXnZX7xATHALAzY6ddxxeXllMktYHcdp7t7L+RzQa/3w9lJXD956B3bFI7pdVKxowZGMLD8b6ueWmX54sK8spFLbEgkc/2fIbO3IVon7b0CPdidNcgHPWn/mkMaOeLv7tjVepeSxsWPgxTucnuQHe2fbP/Gz7d/SkT20/Emj0egJRcMzZby9cV9Hfxp41HG2LTY+06PjnXjM4xEyeda1VtILtsmw3HV8PY15pciAwgb+FCSg8cJOCRRxCOTfuAON9UkFcuWlablec2PIde6ClIvpYI3/prjof7uFal7rW0AcEDcHJw4s8Tf56V6zdF7MlY3o19lzFtx/DcJS+QlleCl4sBi9VGemHJWbln36C+xGXEIWXjHyLGHBM6x0zC3NrYPz6SeRhWvggdr4CYu5rcPlleTtasT3Hp1w/P8Vc1+fzzTQV55aL17YFvicuM464ujyPLvRqsOR7h63rWJkW5Gly5JvoaFh1fdN7XkH039l3CPcJ5dcirZBaVUWaVDIryA6gahG5pPfx7kFeaVyufvi7GXBM6pwyifZpQ3G3jTHAwwNUfNquEQeFfqyhPT8fv7rv+ESmTp1NBXrkomcpMfBz3MaMiRtHOeSigva3XJ8LHhdQ8M9az0GUBcEe3O7BKK98d/O6sXN8e2eZs9mXv49r21+JqcK3qnhrcXgvyZ6u7qpuflmG0P2t/o8cez05HZ8inR4Cd5X3NebDvF+gxCdyb0L1TTe7332MIC8N9xIhmnX++qSCvXJRi02MptZaSkdKPmauOATTYXRPh60q5TXLfd7Hc/8MOVuyvYzFva3mz2xPhGcHYyLEsOLyAAktBs69zJr6LWwmANGmzOCuD+sCKN/nHF+wmPrPlB2A7+HRAr9OzP7vxIH8k9zAAXf262nfxPfOh3Az97M/Dr6542zZMsbH43HzzBV+IrD4qyCsXpbXGjUibnqNJAZgtVsZ0DSLAvf60uIFRfvQI8yIx28Saw5l8seEEFGXC8mfh06HwdhS84gc/3ADJO5rVpru7301xWTHzD81v7mOdkcVH12Ard2XJDi0srD2SiZeLgXb+blzXR1vndvba+Ba/r6ODIx19OrIjvfGfW4pZ+0Du7GvHm7yUEPsVhPaB0N5Nblfxli0k3XmXlhd//XVNPv9CoSZDKRcdKSVrjWuxmqJ4aUIvxvcMbfScdv5uLH5oKFjLWfPxVJzSj8MniVBSAJFDISwGDK6w4ys4Ogp6ToHe/4KgbuBm38zITr6dGBY2jA92fcC+rH3MHDXzTB/VblJKsq17sRa3JyW/hDyThRX707n5kjYYHHS8N6U3DjrBH3tSeWFCV9ycWjZ0jI0cy/s73udY7jHa+7Svt40F1hO46gLtWtSFpC2QeVDri28ia2Ehqc88g2ObNkTOm4uDlx33u0CpN3nlohOXGUe6OYWygp5V9cftYrPCb1MZmbOQjtaj2MIHwH1r4bbfYMIMuOJ1eOIQDH1cmzr/7dUwPRre6Qh/vw6FdXTxnOY//f9DiFsIq42rSSlKOYOnbJojuUew6gooL+5IdrGFH7clYbHamBxzaur/lP4RFFusLNnb8msDXdv+Wgw6A/MP1/9bTJ7Jgs05nnAXO4uCxX4JTp7NWhQk/c03KT+ZTuibb/yjAzyoIK9chOYfno9eOFFe2KPBjJoabDZY9DDsXciezo/Qr3Q2yWO/1N7Uq3P2gtEvwpNH4ZZfYOzrWknbtW/BB30gcXODt4nyiuLzyz8HYHXS6uY8XrMsOb4cgGj3PgB8ti6e7mGedA09tUBIv7Y+RAW4sTDW2OL393X2ZWzkWBbHL8ZUVvcA7ybjfnT6Qnr521GWedsc2LsA+twCjm5NakvR2rXk//wLfnffjUuvXk0690KkgrxyUdmWto0l8UuIdroCV70rPq52rsi040uI+x5G/Jei/g8DNJw37+wF7S+DQQ/AzfPggW3gGQoLbtW6eBrQxrMNnXw68f2B70ksSLT30Zoty5zF/CNzKSvsyvAoraskz1TGlJiaBbyEEEyOiWB7Qi7Hz8IA7JROUyguK2bJiSV17l+fvBGAERFDGr5Q4iZY9n/Q8Upt5acmyvz4ExzbtcP/oQebfO6FSAV5pdUrLi3nWEYhxzIK+TTuK/yc/fEqGUeEj6t9ec8lBVp3S+TPfk0rAAAgAElEQVQwGPl0VRdPcq6JgpIyu9pg8+uIacJsKM6Elc9rvxk04KXBL5FlzmL8r+N5fuPzmMtbLkfdZpMUlWqZQH8m/Ml1v1+HxVqKJXM0g6O18QNHvY6re4XVOve6vmE46AQz/jpKSZm1xdoE0CugFx19OvJe7HusMa7BbCmvukdJeQnrTv6K1RxO79AGyhkUZ8NPd2uLgVz3GeibVmPGmp9Pyd69eI4bh+4fNrO1PmrgVWn1bvtyGzsScxEORbh12ITIH47eWkakvaWDY78AUzaM+R9UFC1z0Al+3pnCM7/uY/UTI2jr13CXwPxYI28uy2HHJQ+g3/qx9sFx7SdgqDtts7t/d3695ld+OvoTX+/7mv3Z+3l96Ou46l0JcQvB4ND8NWF/3pnM//7Yy1WjNrDkxGK6+3Wnt8t9zDpQTK8Ibzyd9YzqHIhXHb/lBHo4M6pzIIt3p2IptzL71phmt+N0QgimdJrCK1te4aHVDxEt7sCTDgzrnc5PR36i0JqJQ95UvFzqeXabDX6bBqYsuHslODd9LdrirVtBStwGDzrDp7lwqCCvtGpSSg6lFXBZ50DK/VaxM0dSnN2HY5Yihra3I+ulrAQ2fwJRl0JYX0ArWhbq7cy2E9oiGYdOFjYa5Pem5JNvLiOh79O09wzSptnnG+G6OeBb95tpG882PN7vcQYED+Dp9U9zw+IbAC198OE+D5NlziI+Px6rtDKpw6RaNd/rsy8lH4vnYpac2MB9Pe9jaq+pvPrHYdwcS/BxNbBw6mCCvZzrPf/1iT3YnpCDe8p6+OUzKMnXfkMpyoDArjDyKfCL1rqsmmhih4kYdAY+ivuI46avAdi1Syvi5lJ4PRaXLvWffOA3OLocrny7WSmTAKYtW9C5uuLSs2ezzr8QqSCvtGq5pjKKLVa8g/awImMlV0XcyryD2mLP4T71T36qEvcDFGfAsC9qbA73PlXmwJ6ZoJXHGPPMtB/yCPi00946Z4+Ah3c2mGY5NGwov17zK2uMayi0FDJnzxzuX3U/AI46rUthSfwSfrzqR8Lca3exnO5Q7gEMvhsZEng1D/Z5sKp9Eb5a91Wn4IZrtAdY0/ko5E8GJ3+JPOaH8AwBV3/wjYLDf8KcSwEB/e+G6MsgsIvWfWJH15hBZ2Bih4kMD7uUgTM/RgDLpt5EtE8Ul727hvaB9fw3kxI2vA9+HaD/PY3epz7Fmzbj0j8GYWj+b0oXGhXklVZNK2iVwZqsT+kX1I+pPacyb8V6gMYza4qzYN10COun9cdXE+HrwuaKeUH21HSpPCa58gOh69Xa2+6sIbB1Nox6tsHz/V38mdRxEqANUO7O3E2IWwjhHuEYC43ctOQmXt70MrPHzG50nOFE6V9gcKSX28012mdXptHhZfDLfQwtzWetrScd7/6dED/fU/tNOXB0JSSsh+1fwHYtU4hO47R8dTvnDBSbHSnL197GHWUwUkqSc81c2qme0gQn1sHJPdo9dM0baixLTcWSkID3jf+cBUHsYfdPQwjxpRAiQwixr9o2XyHESiHE0Yo/fc5OMxWleYy5JpyCf8PJwZm3hr1FuI87DjotCDaYI2+zwS/3akFr/Ixab6HVz01upDqlzSZJqQzy1T8QgrpBl/Gw9VPtA8VOrgZXBoUOItIrEr1OTzuvdjzU5yE2p21mV0bDi3uUWcswGfZQXtSFzHztmaSUGHNNDZZ1QEpY/SrMvRF8I9k2fiW3l/0X4+mJQq6+0GsKXPMRPG2Eu/+Ckc/AkT/hvS5wYJFdz1g9c8mYayKzqJTSclv9H0RbP9V+m+gx2a7r16V48xYA3AYNbvY1LkRN+cj7GrjitG1PAauklB2AVRXfK8oFIyE7DwfXBK5tP5Egt6Cq/nSA8IaC2ob34PgquPJNCKndP1s92DRWnTK9sASLVcumqZV2Oep5sBTD6lfsfKK6TYiegE7o2JzWcB7+n/FrEQ7FlBd2q2pLTrEFk8Xa8IfewcXabzW9/wV3rcC/bVdANNxV5eQBEf1h5H9h2mYI6QU/3w3Zxxt9nurXTc4xV/2M6/wgyonXfsOIuRMM9Y8lNKZ482Yc/Pxw6tj0evMXMru7a6SU64QQkadtvgYYWfH3b4A1wH9boF2K0iL2Zx1ECBsxwacG4sK9XSkwl+PpXE+/a8JG+Ps1baZkPYWtKvvzHfU6TmQV8+CPpxb88HA28OKErjgbHCgtt3L/Dzurjq31gRDQCQZOg80fQdsh0LN5b6Kejp508+vGltQtPND7gRr7Nh3P4octiRTIoxws/xxbmR+6km5sT8jhwR93UlhSXuOZaikvhZUvQEAXmPABOOgJ87EiRN1zBYpKy/lw1VEeHd3x1LqwgZ1hyg/wYV9Y+h/t7451f6gkZBXz1C97AdAJ+HLjCTyc9RVtrOOcrbNB5wAxd9vzo6pFWq1kf/4FBX/8gef48f/IcsINOdM8+SApZeUc55NAUH0HCiHuFULECiFiMzPPb81s5eJxougAAD0DTr2NX9snlJsGtKn7hJJ87W3Tpx1MmFnvYGHXUE9GdAzg2XFdaOvnyoG0Ag6kFbArKY+525LYlZQHwJ/7TrIrKY8wbxdGdwmsewLVqOch4hL45R74/UEobd5Eo4EhA9mbtZf1yetrbP9w45+sKf4vcdbXsMh8Qstv4YnRXfF1c+RAWgHGXBM9w73o06ae3tZtcyD3BIx9FRy0YOukdyDIw7nO8Yj1RzKZvS6ezfGndUF5BGmTk46vhs9GQtqeOm/3xx6trvy1vUOZ2Ccci9VGdrGFQVF+RJ6exWTcpvX797oJPEMa+xHVKXfuPDLffx8At0EX1mLqLaHFBl6llFIIUW+xbSnlZ8BnADExMWenKLeinCaz7DBOTn74u5wa8JvSv54ADxD3IxSmaX3JTvVnmbg66vnmrgEA3D44smp7UraJ4dP/xphrYhB+LIg1Eu7jwronL2X2uniW7j1JYUkZHtV/izA4w+1/wJo3tAyRxI3aOqRhTVuo+taut7IhZQMPr36YV4a+wvio8UgpOVz+Lc6OZTw7+GWuiLwCV4P2NnzfiOjGL2oxaV1X0ZdB+9E1doX7uNTZXVP5QVZnN9aAe7QB51+nwZxRcNkLMPihGh+mxhwz/u5OzLixT/3tKs6GNa9rVSY9guHy5nV3lWVkkDljBk6dOuF51VV4jhvXrOtcyM70TT5dCBECUPFnyy9nryjNZC4rodRwkAjnBoJFdTab9tYacYnWl9wMId7O6ISWRWPMMbHxWDY39ItApxNV/cl1ZuPoHbWaN3f8AeUW+OoqrYpiE/g4+/Dl2C/pE9SHp9c/zZNrn+T5jc9T5pBGV9dJXNfhuqoAb7fYL7WJYCP+r9auCF/XOp+l0dTS6FFw/2boOFab/bv8mZrnNzYIXHgSPu6vBfiYu+C+9eDSvJyPjLfeRpaWEj5zBv733oPOxY602n+YMw3yi4DbK/5+O/D7GV5PUVrM8vj1CJ2F3n5D7TshfjXkHIcB9zb7ngYHHSFeLhhzzSzckYwQMCkmHDiVkdPgYGXkULj3b/AK02rTG7c36f7uju7MGj2LGzvdyM70naxKWo21JIiBgaOa/jCJm7QB4fajoU3tbowIHxfS8s2UWWuWaKh6k28o68jVF6Z8D5dMhS2fwJ6FNc5vcBB41SvajOF7VsNV74CbX9Oeq0Lx5s0ULFmC3z334BgZ2axr/BM0JYVyLrAZ6CSESBZC3A28CYwRQhwFRld8ryjnXXpxOu/tegNbuQeDwi6x76Stn4FbIHS5ukn3siSnUBp/AktCAlJKwn1cSMwu5qdYI0Pb+xPmrb0dVmbkGBvLq3cPhFt/Axdv+OoKWHgHHFqqveHbwcnBiWcHPsuqyauYM2IZphOPEVU9l70xNhts/AC+maAVVZs4u87Dwn1dsUlIzav5PFUTvxpbE1cIuPw17TenJU9AXhLlVhupeSX1v8kf+0srFDdwWrNntYKWNpoxYwaG0FD87m3+5Kl/gqZk19xUz67LWqgtilLleE4Sb+94Dau08mTMk3TytbOGONo/4Ne3TKfAko85aSpRE7wbPyknHo6u0Lol9PYXpsqYMYPsT08FQfdLL6XNiLtZGKfVjn963Klp+D6uBtwcHaqCoM0mKSm3IhCk5ZurHeeIq3so4u61OG54C/YuhP2/Ip29EcOegCEP292+yu4U+0sqW7UPlYOLtA+7az6qtzzBqUJtZtr6uWG2WEnNN1fd8/Q3+eLS8lqLjUidA2mjZhL842jKv53EwaEfYLXJut/k03bDT3dpWT6XPlN7fxOYtm+nZPcegl54Hp1z89Mu/wnUjFflglNaVsbEX+7E0akYDycXbll6Cy8MeoEJ0RMALYifLD7Jzoyd7M7cjZSS/sH98XD0YOGRhWxP20GeJYfSrFE4lIfZV75gyywtDa8Ja4GWHj1K9pzP8RgzBo/LL8dyIp6sT2ZxVaGVn8LG4+XmxJiupxLOhBC08XPjSHohAAtijbz55yGiA9zZkZhbdZybowPdw7wI83HhvclvweWv8tW3X9IjdT4xK5/X6sRc9gLYUaSscqKWXT8D0FI5Dy6Cy16EoY81WIqg8pqVH1q3fbmV7Qnac0T5uxGfVUxavpkQLxf2peRzzccbWfrwsBplE37ZmcITC+MZonuImZaP6fj7BB5yuJrBqTvg2GAQOsg4BEmb4chybcbszfPrLexmr+zPP8fB1xfv6/65y/rZSwV5BYD4PK3QVXJhMssTlxPpGUnPgJ70CeyDi75p/6CyzdkUlxXTxlPLYjmWe4zDuYdp792ecI9w3AwNF/NanxSHNGTQUX8fH0y4kSfXPskzG55hU+omAlwDWHB4AcVlxQC46rV6K/MOzwMgwCUAT7qSeTKIVy+7g+gAT5wNjSzAfOwvLQ2v3x1NSsPL+f4HhF5P8P9eRu+jDfwJRyeYMYMvRo4m5KoRte49uksgH/19jNQ8M3tT8skzlbEjMZdhHfyZ1C+cXUl5fL0pga0ncuhocgcg3wJvHm+DzfY4hweuRLfpA8hLgrGvgVd4g2005pjwcNLXX7mxurTdWn93lwmNBnigqhqnMdfEkfRCtifkctOANgxt709UgBtXzlzPr7tSuH9ke/an5mO1SQ6k5dcI8odOFuCk1zF50q3EllxB3x1P8UTmTxAHxL176mZebbQ5BJe9CO4BjT9LA0qPH6d43XoCHn2k1b/FgwryF7UyaxmbUjfxR/wf/JnwZ9V2T0dPlli0hRv8nP14sM+DJBUkYSw0MrbdWEZFjMLRof4ujcfXPM7OjJ2EuYeRX5pPUdmpvG8/Zz/u6n4X46LG1UhrrG510jqkFFgKO+Lv4s9nl3/GrLhZfHvgW0qtpVzW5jIGBA+gT2AfOvp0BOCP+D+Iy4zjwd6PMnr6Vi6L9mdyTGQjPwAzrPqfNvDn3xFGv2znTw6seXkULF6M57hxVQEewO/fd5M7bx7tNy+nzb9vqHXeDf0i+HD1MX7ekVyjb35wtD/X9NZ+6/h6UwKg9WlLKVm0O5XSchsgSB32BuF+0VpWyoHftK6LXlPgkml1zvY05poJ97Wjbn7mYW2g181fm/Bkx4SgytnDxhwzC7YbMTgI/nN5R/wqFkQf0M6XhbHJTBsRXS3j5vT+ezPhPi5c0zsMCIMBSyHjgNaOnIriQD6R2thACylasxYAr4kTW+yaFzIV5C9SSQVJ3P7n7WSZs/By8uKObncQ6RmJp5MnI8NHYraaicuIY1bcLF7e/DIOwgEfZx/+SvoLbydv+gX1o1dAL27pegsG3am3xEM5h9iZsRNHnSO9Anrh4+xDsGswPQN6klqcyrxD85geO533drzHkLAh3Nz5ZoaEnVrpR0rJ9oz12ErCScvT/vc06Aw83Pdh/t3j35RaS/Fxrp0ud037a7im/TUs3ZtGrqmMyf0jah1TQ1EGfHO1ttBz/3tgzMt2LxNXevw46a+9hs1iwfeO22vsE3o9PjdOIXPGTApX/43HqEtr7G/j58qgKD8W7kiuqqEDp6brV++LNpdZySm2sGC7EUe9Dku5TQuKQx7W0g+PrtRqwvz1EhxbBf9aWKsbw5hjop1/Pc9ls8HJ3dp1ts7WukZu+03LfLFThI8rJ7KK2XAsi9FdgqoCPMDkmAj+s3A32xNyq7qNTs8s0tIlq/W/63QQ3F37u0ew3e1oiuJNm3BsH40hqN65m62KCvIXqbe3v4253MyHoz5kSOiQWotQGBwMDA8fzqDQQWxO3Uxn3874OfuxNW0rvx//nQPZB1iVtIqDOQcZFTEKKuLV9we+x0XvwspJK/Fyqjlg15e+jI8az/G84yw+vpjF8YuZ+tdUoryi0Ov0BLoG4qJ34WTJccryrqtKzzM4aElgrgbXRvO85283Eurl3HCteHMefHcd5CbAv36GDqPrP/Y00mYj6d/3UJ6VRdAzT+PcqfaAsO+dd1KwfAXJDz2E93UT8Z86FUPYqRLAk/uH89j83TXOqZyu7+/uVBXQAVYeSGdvSj53Donkq40JVZOsCOikfQ1+UJvA9VvF5KLbfteyc6CqcuPwjqd1b9hsWvfUxplQkAwIiBgAV38EAR3t/llo7XZhQWyy9lynLRc4rkcwLy3az4JYY9VvLacPxhpzTPRpY8fAeAuQZWUk3f1vTNu24XPrrefknhcCFeQvQmlFaaxNXssDvR9gZMTIBo816LRgX2lw2GAGh2lV+j7Y+QFz9s5h2YllVft1Qsc7I96pFeCri/aO5tF+j/JA7wf4fO/nHMk9QrksJ704HWOhEW/RCWOetuJQWl4JbexcwSk1z8y6o5k8dGn7Gm/JNViK4ccpkHlIW3u1vf0BHsC8ezflaWmEvvUmXtdcU+cxOicn2nzxOVmzPiVv3jzyfvsd70nX43PTTTh37MiV3UN44ff9VTVjQMs5B9DpBOE+LsRnamMO7648gqODjmkjo/lmU8KpUsXV9b5ZS/2c/y+tcub1n4ObP9nFFsxl1pqDrgVp8Pv9WmmByGFaieP2Y5rVz20rLeWyVT/S4fAJHA162peuIm/4cLwnXgtos4In9Arht12pVP7nqN5dk28uo6CkvOGc+BaU8+23mLZtwyWmHz6trJxwQ1SQvwi9v/EPANq7nVmdjlFBt3PkSB86hwuu7K4NWHo6ehLgal/AMDgYmNZ7Wo1tUkomfbqZdH0+lnIbybkmu4P8LzuTkRIm9aunq6a8FObfCsnbYNJXTQ7wAIXLVyAMBtxHNTy5SO/rS/Czz+B3151kfTqbvJ9+Jm/+AkJeeQXv66/j6l6h/LA1CUe9Dr1O4Ot2aowjwseV5BwzFquNzMJSruoZQqCHMyFeLiTmmHh/5RH+NbANgR6n+uALI0awKeIRLo9/GzGjB7a+tzP3qDcTdIVccXgerFsD0gblJaAzwPj3tUyiMyjGlT37M9r8vQg8AvFxdaRkVxpFy5ZhKyzA97bbALghJoK524yAVqDtZEEJ5VYbegddVReO3emdZ6AsNZXMjz7GfdQoIj75+Kzf70KigvxFaNmx9dic3Yk96sTo9s2/zoJYI3/EFbByn47bYi6pv6pjEwghSM410beNN1vicxqeNXmarSdy6BbqWfeHQlEGzL8FjFu1bolu1za5bVJKClYsx23IEBw8Gl49qZIhJISQl18i4NFHSH3iP6Q99xzCyYm7ho7geGYRo7sEkZpXUmNgdEKvUKIC3DiZX8KR9ELuHqotDxju48LqQxkUlpTj5WLgrqGnlg1cfSiDRw724qdJy4gxfoPY9hkPSSs4gkzWQ68bwclLS7vscwv4n1k5XVlWRvbXX2MYPYavet7Ee5N7E+KmJ+XxJ0h//Q2yP/+C8A9m0qdXL67sHsyR9EIGtPNl7jYjafklRPieWlnrXLzJn3z9dQCCnz2z/Pp/IhXkLzKFJWXYnI9iLW5v14pGDakcRCstt7F4dyr/uqTtGbevpMxKekEpU2Ii2J6Q2/isyWqSc810Dalj8ea03TD3Jm0BkBu+hm7Ny6oo2buX8tQ0PB6yfzJSJb2PD+GffIzx3/eQ+tRTRHz8EfPuHVHjGCklhcuXM2zXLsb2748uwAO3W0ZW7Y/wdWVrxbqyp3/4Vf63PGINI2bip6xt+zCvLlzPhzf3pUu7yDNOOzxdycGDSJOJwPFXMe+KU4teh737DjnffUfW7M9IefwJ2nz7LbNu0QqtbTqWxdxtxqqlBk+9yZ/dejFF69dT9NcqAp54vMbYyMXiTGvXKP8wm4370emLKC+Obnx6fSOSc81c3jWIzsEeLNhubJH2pVRMkY/0dyPEy9nuN/nK1ZdqTfopLdRSAwHu+rPZAR6gYPlyMBhqZczYS+fsTPisT3Du2JHkhx/BFBtbtc+SnIJx6lRSHn2MnG++JfnBh0i66y7yFy+uOqb6G2/tVMSa9WLiTS4ck+EEtuvZ4gEewLRDq5Hv0rdm8Tfh6Ijf3XfT5ovPsRYVkXDjjZj3arXhT5V1OJVpY3cOfzNJKcn84EMM4eH43X574ye0QirIX2TWG7WVg9q59SGlCV0hp6vM3IjwdeWGmAh2J+dz6OTpa8E1XWWwivB1JdzHxe7fNjIKS7FYbYSf3r+77h0oSofJ355xrZPCP5fjNmggDl71Dyo3xsHDg4jP52AIDcU4dRoFy1eQu2AB8RMmYNoeS+BT/yXgicfxHDcO15gY0p55luNXjefIwEEMf+sROuYmAbWXHEw+bXnB5FwzLgaHGn39LcVmNlP4118YIiIwBNa95qpLjx5Ezv0RnZMTibfeRvGmTQR7VVTorNZGu3L4z4B5505K9u7F7757EY4t/7P4J1BB/iKzM2sTNosfw6M6kFVkwWQpb/ykOmQVaZkbET4uTOwThsFBsGB78hm3r/K3iwgfVyJ8XBuu2FjjvIoPh+pv8klbYdMH0PsWCI85o3aVHDhAWUoKnmPHntF1QBuUbfPlFzi2bUvKI49w8oUXce3Th+g/FuN3xx3433MPYe+9S9jMGXhccQVO0dF4XDEWR3MxT8b+iJA2knO1iVKVjKfloVeW6z0bATTt2ecw79hRNbhaH6foaCLnz8MQHkbqc8/hYCnVKnRWa6Pd5RaaqWjNGtDr8bzyyrN6nwuZ6pO/CJgs5bgYHEgtTiXJvBuKLqNnuPY2GpuQS9sGslfCvF3QO+hIyzfj6njqV+vdRm3lowhfV3zdHLmiiy/rd+7FckUnHBsrI9CA5FwTjg46Aj2ciPB1JaOwlGMZhRgcdIR5u5BvLgOomnSTbyojz2yp0R5Ay4X/5R7wioAr3mh2eyqZtmslf92GDTvja4E2IBs5by7pb7xBWUoqYR/MROfkVOMYvY8PYdPfrvre0rUn4S88y5DUvWwI68X+1AI8nPXIapUgjTkmErOLScgqps1ZyFox795NwdKl+N8/Dd9bb2n0eL2/P8EvvEDSbbdjnHY/UTG3EZ9VTGJ2Mcm5Zoa2b/mupOqK1m/AtW9fHNzdz+p9LmQqyLdyxaUWBn5xJ55eJzHbtOJRwboRVbMgb/tyW4Pn3zaoLSM7BXDX17G4OTqw/bnRZBVa+Pe3sYCkz9EPYclCPihKRyAxffA6joP+DYMebFZ63pGThYT7uKDTCSIr2jj6vXUADO8YwLoj2tKRG/57KV4uBoa9vZqCinxzvU5oZX0zD8O8m6EgBe5YAs51DMY2UcmevehDQurtnmgOYTAQ/MILdh8ffvVVrHl7Bs9u/47XkYz/sOb+qAA34jOLGTF9DUDtSVBnSEpJ+tvTcfD3x+9u+9dTdRswgJDXXiXt2ecY6xXFcy59q9rYzr/uD6Kykycp3rwFbDZsJWZsRcUgBJ6Xj7G79nt5djalhw4R8Nhjdre1NVJBvpVbnbAd3PZgLu7MrX3H89tWPZFeYfQI82LObTEUlpTVe+6na49zMK2AIE8tH7vYYmXp3pN4Viyq/H3nzfju/Ag6XoHZvyfvrk3l3w4HcV3xnNYPPuaVJgX6zMJS1h/NqkoNHNstiI9v7ktpuZU5609UBXiAo+lFpBeUUFBSztNXdibAw4lwH1ecjy2FX6dq0/tvX1znYhfNYd67F5cePVrkWs3l4OxEyNx56J98kP87/ieT7r4Gq6c2W9TgoGNQtB8bj2VhtUmEgBEdW+4DCaBo9WrMO3YQ/NJL6NzsKwFRyfv66ylYuoyY7cuZMeNf2PSO6B10jO5Ss43SYiF37lyyPpmFNT+/1nWyP/2UqCV/YAhpvJBcyb59ALj0af5YTGuggnwrtyrxb6TUkZNwI4NGj+SLjFgu7acNdlUvg1uXjcey2XgsC2OOCT83R7xcDCyINXJlF1+u1G1lSNKn0PVauOFrXIB5G5ZjbTeNl/RfwaYPtfVBRz1ndy2UX3clU26TTK5YSclJ78BVPbV/zNsTcjiYdmpgNznXxC+7UugQ6M69w6O0vucN72t1XML6weTvtNWVWkB5bi5lRiM+Uya3yPXORNcOoZhefJ6k2++g6/P3Ezr9bdwGnvog0wp9tTxZXk7GO+/iGBWF96Trm3UNn1tvoXjqNEabjbiPGFFrv81iIeXhRyhaswaXPn0IeuYZHHx80Lk4o3N3pzwtjfiJ15H+5luEz5zR6P3M+/aBEDh37das9rYWauC1FZNSsjNrA1ZTFI7Clc/WxVNUWm73YFeErwvphSUczywiwteV+zqXcLVxOjf9PZxZjjMhsCtMmAFCIITQcp/zzHDldG1Ztx1fwcxesP49sDY8wCulZEFsMn3beNM+sPZEo8raLo4OOhwddKw+lMGupDym9I/QAvyWWVqA7z4J7ljaYgEewLxjBwAuvS+MN0LXPn2IXDAfnbs7SXfeRdbsz+o8rjw7G0tyCtbCwjO+p2n7diwnThDw4AMIffPeDd0GD0bn7k7B8hW19tlKSkh+4EGK1qwh+KUXiZz7Iy49uuMYHobezw+dkxOOkZH43iwJLEgAACAASURBVHYbhStWYDE2nrJbsm8/jlFROLg37beO1kYF+VZsb9ZecstSkEW9uL5fGKsPaeus2zuNPMLHFSlBl7iJ9wufYErsFG5wWMfisgG86vpfxL//qrGAcriPi5a/rdP9f3vnHR5VtfXh98xkJpNJI52QQugt0qRK7wgiYEdE7B+Wq1xAL4pw7Ypiu1exXhuiSFWKIIj0DtJLCD2FFNKTmUlmMvv740xCQnoykSTu93nyZOacffbZa5L5zZ61114Lbp4LU3ZA05tg48vww11gKfn1u4CDMemcScrm7jKyRxaMOdTXjVBfNzZFJeOiURjXJQQOLoR1M9U86OM/KzXlbk3I2bsXxWDA0LGjU/utCYa2bWm2bCleo0aR/P77JP/3I658+ilRPXpy+qY+RA8YSHSfvpwdOpTo/gPI3r6jRvfLXL8exc0Nj0HV2yMAoNHr8RwyhMy1azEfO17s3OUXXiBn+3aCX3sVn3vuKbMPn3vvBa2WtB8XlXsv87Hjap6ayMhqj7ehIN01DRQhBAtPLkSDjsbantzTPbwwh0iltpHb7XRJWcU83a/cptlGpgiG4a/x7Ml2rIzOZWhQILgUjwYJ9TGy62wKQgh1dh3UXq3ic+BbWDMNvhymJgXzbV7idov3xWDUaxndsfS84QWhkQVjP5+cxXNhUfgvfEvd0dpiMNz+P9A6/1/atHcfxq5d0NSxOGuN0UiTt+eCVsOVj9V8LO79+qELDUGYLehbNMfF14+Ur78i5pFHMLRvj/fYW/EaPRoX/3KydF6DsNnI2vA7Hv37o3GrWchjwPRpmPbtI2bKFCIWLUIfGoIlKorMX9fi9/gUGt1xR7nX64IC8RgwgMxVqwicPg1FWzKSKz87m9gnn0Tr7Y3/U0/WaLwNASnyDYCTlzPZfzGNFuFxbI7ZzImUE1zIvEBGbgbeuSMIa+RLx1Bv2gR5EpWYRWhF28izEuHnKTQ/+wdNNDoW5Q/Epf9b3HVTW0Z7J7Ay+kCh+6QoYb5GcvLySTNZi2/CuXGyKuyLJ6npcIfMgQ63sTXGys+H4gBYdyyB0TcE4+Hqorp2Nr4Ex5ar17cZRbPWt6HDRltPMx0ytjBLv4jWiXFqsY/hr0O3h0p86DgDW3IyuadO4TV1qtP7dgaKVkuTN97AvVdvNJ4eeA4ZgqIp/gXdc9hQMlasIOOXlSS++RZJ775H0+8X4FbJbybZ27aRf+UK3mNuqfF4dYGBhH3xORcm3EvMo48S9vlnJL71FhqjEb8HHqhUH95jbiF740ay1q8vNf49+YMPsSUlEbHoR/RhFdQV+BsgRb4BsGD3RZZELcUQvBw3Fzfa+bZjeNPhtGjUgreX+BN2g7rQ+szQVqw5crn8RGJxB9RUvLlZ2Ee/zyOH2pOQlcunrdTF0MFtAxnSNpDBbUtGboQVqflZYqdls37wyEZY+iCs/iesnYmfS1v6mb2J0zfnSTctY5r0gASNuoB6bJnqfkGBgwtotO8Log2A41t+jKE5+bf8D23keLU2ay2RvXUbAB4D+lfQ8vqhuLjQ6Lay0zVovbzwnTwZ38mTyY2O5tJDD3N51ix8H3oYt86dcPH1RePpWeLDQdhs2LOzSVv4A1o/v1IXS6uDa4sWhM3/mEsPP8LZYcMBaPzqK5XeSewxaBCurVoRN+NZbCmp+Ey8t3DTl/nIEdIWLsRnwgTcOnVyynjrO0rRXXN/Fd26dRP7i+TtkNSMif/bziFm0CmwDd+M+qKwNF+WxcoNL63nXyPb8vjAFhV3lHoePh8IBm/VzRLYrsJLrElJZK5ciengIbLPnOUTr84Mf/EpbulUxsKnEBB/EA7/yPH9m2lGPEZ7dsl2w16BPs+ojy0ZcHI1ZMWD1hVaDIKgyBqlya0MlqjTxDzyCGg0tNy8qVa33/+VZP3xB/Ezn8eeeTVaSdHrce/dm5AP3kfk20lfuoS07xZgjY8HIPC55/B7qPJFziuDNS6OzN/WowsJwXP4sCq9vvnZ2cQ/+xzZmzbhfcftNJ4zh6z1G0h84w0UFxea/7qmQW6AUhTlgBCiStu35Uy+AXDOtAuNVw4Dgu4pVnu1MJVrZbL82fPVghNCqNWFfJtVeIkQgtgnn8Jy9Cj6pk3ReXry+NGfObrlBuh0b+kXKQqEdCU/uAvjdqzl0b7hPDcoHOw2tTh1yhl1l2p4z6vXGLyhy8SKbXAidpOJmMenkJ+TQ8CTTzYYgQfwHDyY1nt2YzlxgryzZ7GlpmKNiSVt4UISXn2NnF27sF2+jFu3G/EYPBhhs5Yoc+gMdCEh1f7g0Hp4EPrxRyT/5z+kfPoZGUuXFZ4L/ei/DVLgq4tTRF5RlAtAFpAP2Kr6SSOpPna7IINjKDZP3O1ti50rTOVamYXWw4vUYhrjP6uUwAOYDx7CcvQoQbNfxHfiRITVyqabhhC6/FvEPyaUK4wJmRas+YIQX8+rO1KNvjVKIuZMrnz6Gbb4yzT9fgHGbg3v31lRFNw6dMCtw9UYcrvFTMYydR0k/JtvcO/Vs6zL6wSKRkPg1Km4RUaS9M48jL16EfCPp6q0qPx3wJkz+UFCiCtO7E9SCZKycsE1hnxzGLFpucXOFSb7Ki9kUgi13uf62RDaHTpWvixa5upVKEYjjRxV7xWdjj2dhzBu2yJs8fHl5u4uzDb5F5V+qwq5586R8vXXeI8d2yAFviyCX3kFQ/v2uPgH1HmBL4rn0KF4Dq16la+/CzJOvp5zOjkZrWsydnNIsfSzFms+MakmjHotPsYyFlrzbfDrDPUnoq+6S7QKbomcvXsxdu2KxnhVqC1t1Jlh1sFD2PLtV29lF9jy7WRZrMSmmTgWp8bM12bpt5y9e4mbNp2c3XvUMWTnYImKUt0TDl/ztQghSHj1VTQGA4HPzqi1sdVFFK0W34kT8Rox/HoPReJEnDWTF8B6RVEE8JkQovQteBKnsy/+CAA+LleLgKw5cplpiw/RLtiLMJ8y8nVbLWo5vDMb4KZ/wNBX1E1MlcSWmkrembN4j7m12HFDmzbkanUs/34dUeZQ3rlTjXCY88sxziRlczoxizSTmi9Hp1Vo0si5G5dALU2XunAhSW/NBdRiH8Zu3TD/+SfCai20M2DqVPwfe7TYtVlr12LatZug2S/Kr/2SBoGzRL6vECJOUZRAYIOiKKeEEFuLNlAU5THgMYDw8HAn3VZyMHkvQmjoEdyJndHqTP7bnRfItdk5FJNeIgFUIb+/pAr86Pege+UzChaQvWkzAMbu3YsdD/X35HSjUMLOHmHenzG8MKodPu56DsemcyxOjea4p3sYXZv6EO5rxNXF+eGPyfPnk/LJpwCEvPcuqQt/wJacjM+kSeiCAsmLicWWmEDyBx8gLBZ0YWEY2rRG37IliXPfxtC+fbm7LiWS+oRTRF4IEef4naQoygqgB7D1mjafA5+DGkLpjPv+3RFCcDpnO9rcVrRt2phVh6I4FpfB3guphW1K27RE3J+w5xPo8Vi1BN7050GSPngfQ2Qkbp2Kb6gJ9TXyVfM+vLDve8ae+oNfDnXggT7NilV4urVTE25qWTuzZNP+/aR89jmeN4/E//HHMbRujdeoUSXa5WdlYZ38AFfmzy88ZuzZE1tiIsGvvVbqTkqJpD5SY5+8oijuiqJ4FjwGhgPHatqvpHyEEHx9/GvMIokApUehb/uD30+j1SiFrvVSfd7rZ4N7AAyeXeX7Zm/fwcX77oN8O8GvvFxCDMN8jGxr0onNIZ25/+Q6TixeRZbFSrrpakrj2vLD52dmEvfcc+hCQwl+9TUMrVuX2Vbr6Umz5ctoc+ggLdatxefeCZj27EHfvDnuffvUyvgkkuuBM2byQcAKh9/XBfhBCLHOCf1KysCab+W1Pa+xPHo5LubOtPceWLjb9PeTSQxtF8iJ+EziMywlM05e3AUXt8PIuVUuppF7/jxx06bh2qoVTRcuLDW7X6iPGygK73e9m0hNDhN+/5LNv1zdVKXVKAR714If3m7n8ouzsSUmEfFD6WMrDY3BgD4igsZz5uA5bBhaP78GFRMvkdR4Ji+EOCeE6OT46SCEeN0ZA5OUzcu7XmZ59HIeiXyUjIt30dTPu5hb5s5uYYUFrYuFKJpSYe2zYPSDruXX57wWW0oKsU88iaLVEvrxx2WKqEGnJcDTlTytjpZffEq6wZOAN2fhmZcDQGMvAy5a5wd1pS1aRNb69QROn17t7ezuvXuXO/uXSOojMoSyHnElO5e3N+xl1bnVhLsM58zpftiFhjAfI/4eetx0Wvw99AxuG1go7oW7XXNS4NtbIfk0jPsU9JV3maQt+ono/gPIi4kh5MMP0IeWn6s9zMcNfw9XgiOasO2+GXhbshh9fhd6raZyu2+rQfqSpRgiI/F98IFa6V8iqa/ItAbXkYUnF9Lcuzm9m/SuVPulf0bz9em5uLjbOXO+M2n6VJr7u9MtwgdFURjfNYQ2QZ7otBqGdwjCYsvH06CD7CRV4NPOw4QfoeWQSo/RfPQYCS+/jHvfvgROn4ahbdsKr7mlYxMSMi0AjL1jEMdXt+eOizvxGz+Wxq0qLttWVXKjo8k9eZKgWbOkq0UiuQaZoOwvYn/CfubsnIPNbiPYPRhPvSdbYrdg0Br4ZuQ3dPCvuETZhMWvccz8E5aEMdza7G7evasSbglbLnzWX80LM2ERNK98JkFhtxP3zFRydu+m5aY/qp0PxBIVxcX7JqEPCyNi2VKnC3Him2+S+sOPtNq8CRc/P6f2LZHUJaqToEy6a/4CEnISeHLjk2gVLTcG3YiiKESnRTMiYgQ+Bh8e/O1BTqWeqrCfc+Zd5JvCsab1qbzb4/AiSD6lFtSoisDn53PpwYfI2rABn/sm1ijhk6FNG4Jm/gvLiRPk7NxZ7X5Kw5qYSPqKn/EaNkwKvERSCtJdU8vk2/P5985/IxB8MvQTQj1Di51PNiVz56o7eXnny3wx/AsMLgbeO/AePRv3ZEDYVVE+duUYJi5hzVJjviuV8yU3C7bNg+DO0KZkcYXySF+yFNOePQTOmI5vJYs5lIfXmDEkf/Ahl1+YReCMGXiOGI6i0yFMJoRdTX+g8fCo0iw/a+NGLr8wC2G14vvQQzUeo0TSEJEiX8t8ffxrdsbvZHav2SUEHiDAGMDMnjN5bstzjF4xmr4hfVl5diULTizgic5PYLaaiUqL4nDyYbA1wpbRFahkrPm65yEjVs0sWQXxtKWlkfT++xh79MD34Yed4l7R6PWEff4ZcdNnEP/ss2hffx2try95585dbePpiT4iAn2zCHzuvhvjjTeW2pfdYiHp7bdJ++FHDO3b0+Tdebg2q1zmTInk74YU+Vok2ZTM50c+Z2j4UO5sfWeZ7UZGjCTMI4y5++ay8uxKmnk1x1XrxvxD89FpdLRs1JIBIUNYvLEVIl91m5TrrrFkwLZ34eAC6DddLaZdlXG/9z72nBwaz37Rqf5zQ7t2NF+9ipxdu0j/aTHWy5cJmDoVxdUV7HascXHkXThPztZtZG/aTNPvF6Br3Bh7Tg621DQyli/DGn8Zy+kobPGX8X3wQQL+ObXO1V6VSOoSUuRrkY8OfYTVbmXajdMqFMsO/h34duS3/HFpK49/E4PVJmjbKoK0+F58d88YohOz+WntdsJ9jSRn5RLoWcaGopwrsGA8JBxVy+cNfL5KYzYfOkT60qX4PvAArq1aVenayqBoNHj06YNHn7J3leZdusT5227n/Nhxxa91dUXfojmuERE0eePNepUOVyK5XkiRryWiUqNYEb2CSe0nEeZVuWLCiqIQbuhKnlkth3fqZD+EgA0nEnHRqB8Sb952A54GF7SaUj40MuPhu3GQfhEmLoVWVcuxbc/LI/7FF3EJCsL/ySeqdK0z0YeH0/zXNWT/8QciNxeNhwcad3fcOndG17jxdRuXRFIfkSJfS8zbPw8vVy8e6/hYla6LKZITviC6dfH+GPq1UhN63RDqXXoh7qwE+Ga0GhN/3zI1P3wVyVq/gbwzZwn9+KPrXj5NFxgoM0FKJE5AinwtEJUaxe7Lu5nRbQberpWrQF9AQV1WD1cXsnNt6LQK289cQa/V4O2mK13g862w6F7ISoT7f4awHtUad8aKFeiaNMFj0KBqXS+RSOoeMk6+Flhzbg0uigu3tri14sbXEJtmwtVFQ+ewRgDc1U119Ww8lVQy2VgBW96GuAMw7uNqC7wtLY2cnTvxunUMShWKh0gkkrqNfDc7GSEE6y6so09IH3wMPlW+PibVTKiPW2GIZK/mfvRpobpqSo2NP7dZjYXvdC90GF/tcZv27QMh8Ohf+Q1TEomk7iNF3snEZsVyOecyzdzVGO/j8Rks2nup1LYrD8dzOCa92LGYNBNhvsbCEMkwXyN3dgt1PHbM5IWAS3tg9TT4cQL4t4Gb59Zo3Kbde1CMRtxuiKxRPxKJpG4hRd7J7EvYB8DRM+oW+1dXn2DWz8eKFbUu4MUVR/l0y9nC58lZuUQlZNE+2Iv+rQLoEeFL6yAPRnRozE0t/BjQOlCtzfr97fDVcDj0A7Qdrfrhq5gbvijZ27aTuWYNxhtvRNGVUfRbIpHUS+TCq5PZGb8Xu82dlCwfLqbksPucWorvcoal2C7VDJOVTIutWFm8FQdjsdkFt3UNpWWgB4unXM1O+cOjvdQF1sX3w9mNMPw16Dq5RuIOaiGQ2GeeQR8SQtDMf9WoL4lEUveQIu9ETFYTW2M3k5/dhrhMC0sPxBaeK3DDFH1e9LcQgp/2xXBjUx9aBpYSvmjLg1+ehKhfYdQ86PFojcdrz8sjbvp0NDodYV98LmPQJZIGiBR5J/Lbhd8w5+dgTe9OqjmPH/fG0DLQgzNJ2cSmmqHF1baxDnFPN1nJslg5nZjN2eQc3r69SKN8m7qwGr0eTq2BzFi1LqsTBB4g6Z155J44Sej8+VLgJZIGihR5J5FjzeHjQx/jr4/gvDkCUCs5zRnTnqmLDhaKegEF8fAFjxfvi8Go1zKqo6OohhDw8xQ4ugRc3KBZPxjzYZV3sRbFmpSE5cgRtL6+ZG/aTNqCBfjcPwnPwTIuXiJpqEiRryG5tnwyTFY+OvI+SaYk+rm/zHnUlAO+7npGdmhMsLcbB2PSEUIU5rApurP1YEwaq4/EM/qGYDxcHX+SI4tVge83Hfo/B7qaFb8WQhD/3L8w7d5deMzYqxeBM2bUqF+JRFK3kSJfQ8Z/vJNTaccxRixGZPQmLjuQkEa5xGeYGd8lBL2Lhgh/I9uirzB3XRQzb1bL50UnZhPSyI24dDOzVhwD4O7ujhw3mZfh1xkQ1gsGzQKNtsbjzNm6FdPu3XgMHoz3rbfiEhSIoUMHmcFRImngSJGvAXk2OycT0ghsuxKh+JCUOJzDIp0R7Rvz9h0d6eTYtfryrZEMfW8Lx+IyAIhPN7P7fApPDWpJ13Af4jPM+Br13NjUsXlq10eQlwPj5jtF4IXNRuI776BrGk7oB++jSGGXSP42SJGvAfHpZnQ+2zERw3Od32T2CQWBummpT0v/wnYtAz0Y3TGY4w6RX3YgFiHgzhvDCPe7Zhdr7AHY/xXccAf4tcAZpC9fTt6Zs4T850Mp8BLJ34wGuRlq4cmFPL/teUxWU8WNa0BMqgmd31YifXpyR9ur5fVKq9oU5mMkLt2MLd/OkgOx9G7uV1LgL+2B78aCewAMmeOUMeZdvEjyh//BrWtXPIcNc0qfEomk/uAUkVcUZaSiKFGKopxRFGWmM/qsDkII3jvwHm/tfYvV51bz9Kansdgsxc4vjlrM2vNrybfn1/h+x5MvonHJYUBof9z0Wvw9XAFKTSQW5uuGNV+w8nA8l1JN3NX9mlKAF3aoxT48AuHBteBdslRgVUl45VXOjhgJNhuN//1vp1Z5kkgk9YMau2sURdECHwPDgFhgn6IoK4UQJ2rad1UQQvDq7ldZcnoJd7e5m0j/SObsmMPMbTOZN2Aeey/vZcHJBWyP2w7AsuBlPHrDo2TnZZNoSqRTQCc6+Heo0j2PJ58EoHuTGwBV3K9k55aaSKzg2LvrT+NpcOHmyOCrJ+MOqKkKGoXD5JXgWfOY9Zy9e0n74Qe8Ro0i4Jmn0TdtWuM+JRJJ/cMZPvkewBkhxDkARVEWAWOBWhH5FHMKHnoPXLWuxY4fuXKEJaeXcH/7+5nRbQaKopCRm8G8/fPo82MfTDYTPq4+TLtxGl56L97a+xaPrH+kWB9aRUtrn9bMHzoffzd/KuJ81mkQCu382gCqm+ZQTDqhpYh8wew+Lt3MxJ7hGHSOBVW7HX59FtwawQNrwCOgOi9LMXJ27iTm/6aga9KE4FdfQePuXuM+JRJJ/cQZIh8CxBR5HguUKL6pKMpjwGMA4eHh1bqREILpW6aTlZfFlE5TaOvbllCPUBRF4eczP+Pm4sYTnZ8odEvc3/5+fAw+7L28lz4hfRgSPgS9Vl14HBI+hCNXjuDn5oevqy8bLm4gxZLCwpMLmbl1Ju8Neg8vffl5YZLzzuGqDcKoU0V9aLtA8u123PQlI2LCfI10CvXmSnYe9/eOuHri2DJHLvhPnCLw9rw8El5+BV1ICE1//EEKvETyN0cRBTXmqtuBotwBjBRCPOJ4PgnoKYR4qqxrunXrJvbv31+t+22N3crsHbNJtaiJv3xcfRgYNpA159YwstlIXu/7erX6LeDnMz/z0s6XCDQG8nrf1+neuHup7ezCTqevexPq2p21Ez+q3s1sufCfruDuB49uhkoU67CbzeRduoRry5Yo2pIfJilffknSvHcJ++JzPPr1q964JBJJnURRlANCiG5VucYZM/k4oGil6lDHsVqhf2h/1t+xnui0aKJSo9gWt40VZ1YA8ETnmhefHtdyHM29m/P8tud5+LeHeSjyIf7R5R9or4lXP5J4ErQmWnl1rv7NDv+o5qMZ+98yBT4/PZ2MVavJ/PVXsNvJu3iR/PR0cHFBFxyMvmlTAqdPw9CuHdbEJK7M/wSPwYOlwEskEsA5Ir8PaKUoSjNUcb8HuNcJ/ZaJq9aVSP9IIv0jub317WyJ2YJeqyfEI8Qp/XcM6MiSMUuYu28u/zv2P1o0asGYFmOKtdl4YQcA3YKq9KF6ldRzsHkuNOkCzUvPHZOxahWXZ72IyMvDtX07bAmJ5Ken496vH4a2bbHGxZGzby8X75tE89WrSJr3LsJqlSmDJRJJITUWeSGETVGUp4DfAC3wlRDieI1HVgUGhDm/ZJ1RZ+Sl3i+xP2E/y6OXFxN5IQQbYn4l3xJMh6BqRK2YUuG7cWAzq0nHSglttOflkfTOPFxbtSL4tVcxtGuHLSWFnO3b8brllkJXTd6lS5wdNZozgwYD4P/UU+irueYhkUgaHk6JkxdC/CqEaC2EaCGEqJlTvBysVivJsdHk2eykZOeSkp2LOa/y8e75dkFqTh6VXYdQFIXbWt3G/sT9LI5aDFyt4RpnOos1rTdhZRXXLnMQjsIfWQlw7xII7lRqs6wNG7AlJREwdSqGdu0AcPHzw3vs2GK+eH14OI1uuw0Ar1tuwf/xKVUbj0QiadDUq7QGhz6+n4j0XYzx+paopGwAPFxd2PX8YDwNFZet+78F+/n9ZBJPD2nFtGGtK3XP+9rfx59Jf/Lq7ldZfW415zPOk56bjo82gsScrgR4ulbcSVHWPQ8XtsG4TyGs9EVdANOevWg8PXG/qXeZbQoIenEW/lP+D12TJlUbi0QiafDUq7QGWf5dCCANW3IU49u48mq7GPpYd3IhOafCa2NSTWw8lQTA0dj0ClpfxVXrygcDP+DO1ndisVkYHD6Y2b1m08b+AqGNvKu2izRqHez7Ano/BZ0nlNvU9OcB3Lp0LjWC5lo0er0UeIlEUir1aiZvi+gHZ2Cj67NwUT02SQ9ndljg7rdK9W0XUFCK74YQb2KK1FWtDDqtjjm9i+eS+fa3baXmqCkTqwXWzwK/VjD0pXKb5qenk3fmLN633FKlcUokEsm11KuZvG/IVRdLXOepZN+7kp9sA2l56lNYV3bKHLtdsPRALH1b+tOzmS+xaaZK++XLIibVVGr6gjLZ9BqknIGb3wJt+a4l85EjALh17lKTIUokEkn9msmH+bnzdN5TuCp5zBj8PB5eBt5wMRHh70vPPZ9C+3HQtKQPe8fZK8Slm5l5c1tSc/KwWO0kZ+cS6Fm9aksZZiuZFhthvpVcdE2/BLs/ga6ToWXF5fssx9XgJEOH9tUan0QikRRQr2byAR6urNP05RdlMAEFGR99Pfif2wPgGQybSg/sWbw/Fm83HcPaBxXmkClaY7WqFNRrLS1HTQmEgC1z1ccDnqtU/+bjx9E3bYrW07O6Q5RIJBKgnom8RqMQ6uNGqI8bGo3qfw/zMbI+OpszLe5Xo1Zi9ha7Jt2Ux2/HExjfJQSDTlvoR7+2sHZVKPiAqNBdY8+HDbPh4PfQ64lKpQ/OXLuW7N83YuhQtYyYEolEUhr1SuQBRkUGc3Pk1VS8A9uoSb2ev9BVnc1/fwec21J4/s9LaeTZ7IXXFMzkY6u4+FqUgg+Ict01uVmwaCLs/C90fxSGvlxhv7a0NOL+OQ0AY4+ywyslEomkstQ7kZ8xog3PjmhLfnY2CW+8QY/Xn+bhyEZEpSvw8AbwagLf3waHFwFXZ93NAtRsjEadltbuJmJTsqo9hphUE56uLni7lbGAmnMFvhoJ0eth1DwYPa9SyccyV64EIPybb2h0113VHp9EIpEUUK8WXgvI2ryZhDn/xpacDECvXav4n2c/Mlwb4/3wb/DTffDz4+AZTGxaAK4uGgLcdXBxF6x6hvX5UWSc8Ib9L0OnCaCr2gJsbJqZUF9j6THylgz1QyblDExcXKmFVlB30qYvW46hY0fce5XI1CyRSCTVot7N5O0WC3H/nIbW25uInxbhfdt4Qjf+wq1nt6luFIM33POjGo++eBIhF37mXbevUN5rB1+PBEs6y/0f5wIh0E+j9QAADo9JREFUsHoqvNMSVkwp4csvj5g0U6kl/kg5C9+MhsTjcNeCSgs8gOXYMXJPny5MUSCRSCTOoN7N5HN27UKYzQT+61+4deyIa5s2pEedZdzZ7cSkmunQxBtcPWDiEvhmNA8kz8WsuEGrEdDmZmg9kugtCczc1peTD7ihPbEcTq5S0/72+D8Y8Xq5cexCCGJSzfRtWaTAh9UCp1arKQvsNvVDpvXwKtmVvmwZisGA1+hR1X1pJBKJpAT1biafvWkzGqOxcGFS4+qK16ABBJtSSIhNvNrQpyk8tZ97eJN5kSvhrm+h0z3g1ogwHyN5+QoJATfB2I9h2kk1+mXvZ6qrxWopcd8Mk5V0Ux4XUkyYrflXF123vgPvtoZlD6sfLg/9VmWBt5vNZK5eg9eI4TJsUiKROJV6N5O3Z2fhMXAAGr2+8FijLp3IAkxHj8HIq1kdM2xadluaMjjAr1gfBQIdk2oipJEb6N1h5JsQ1AF+eRJ+ewFGvQOOQiGfbD7L3HWnivXR1M8IF3fCH69By2Fw01MQ0b9SC6zXkr15M/bsbLzHS1eNRCJxLvVO5EPeew9htxc75uaIKdeeiSp2vKxNSwXx7TGpJno1L/IB0OU+1Z++ez4kHFFzvQd14HBMOkFerkwZ0AIAo15L3whP+GoGeIXAXd+BvgopDq7BfPgIiqsrxm43VrsPiUQiKY16J/IAyjWzZa23Nxne/rjHXSh2vKxNS8GNDChKGbHyI95QqzWtmwmf9YfmA7FeuZt2wRE82KfZ1Xa/zYKk42pO+BoIPIDl5ElcW7dGcamXfw6JRFKHaTCqYglsgmdKIkKIwtDGsjYtubpoaexlIKa0Xa+KAh3vUiNjtr8Hfy7gDcufHHS/B86ZIS9Hne3v+kjd5FRF//u1CCGwnDyJ180316gfiUQiKY0GI/KEhBJ08QwpOXn4O/LalLdpKczHSGx5+WuMvjD8NbJajyf+q8cYGT8fvpt/9XzrkTDslRoP2xoXhz0zs7D6k0QikTiTBiPyhqZN8d5sIuZiAv4d1LqrMeVsWgr1dWPX2ZQK+72oa8n4vFf4ZmwAAxvnqhE0bj7gE+GUcVscaYUNkZFO6U8ikUiKUu9CKMvCu5XqL0+KOgfAtuhk/jiVVPqmJdTF2IRMC7k2tUbsD3sukZRVPHTSYs3nhRVHAfALawPN+qn+eicJPIDp4CEUNzcMbds4rU+JRCIpoMGIfEgHtaDIhcNqqOO839RIm/6tA0pt3z7YEyFg7/lU4tPNvLDiKEv2xxZrs+d8KkdiM9C7aGgR6F4r4zYfPIhbx45y0VUikdQKDUbkPVo0I9dgxL5nF+a8fGLSzEzoEc6kXk1LbT+obSCNjDp+2hfDpVR1ATYmtfhCbMHzLc8OxKh3vgjbLRYsJ0/i1rmz0/uWSCQSaEAir9HrUQYNpVfMIZbvOE1qTl65qYBdXbSM6xzC+uOJHIvLACgRbROTZkKv1RBUzQpSFZEbHQ35+bIClEQiqTUajMgDtJp0D4Z8KwcXLAMqLupxV7cw8vLtfLntPFCyWlRsqpmQIgVKnE1ulOpSMrRtWyv9SyQSSY1EXlGUlxRFiVMU5ZDj57pm13Lr0pmcwBB6RO0CKKwCVRbtm3gRGeJFQqa64BqfbibffrXAd5nZJp2EJeo0itGILrTiilESiURSHZwxk39fCNHZ8fOrE/qrNoqi4Hf7eG5IOUdwzhXCyhDo/IwMLKdOIex27u4WVnjcZhecTc4m02Il02IlJtVU4QdFdRFCYDl+HEOrViV28EokEomzaHDqEnrPHdgVhVFxf+Lrri9xPnPtWqIHDOT8uPFcnj2bMe0DcXXRqInKgOHvb6XjS+vp+NJ60kxWmtaSyCe+9jrmP//E2KtXrfQvkUgk4JzNUE8pinI/sB+YLoRIK62RoiiPAY8BhIeHO+G2paMLCkLfsxe3RR9EWCzg6krWxo2YDx7CfPgw5gMHcOvcGbfOnUn95husMbF8P20OQWHB7Dh7hZxcW2FfLhqFcV1CnD5GW1oa6YsX4z12LAFP/8Pp/UskEkkBihCi/AaK8jvQuJRTs4DdwBVAAK8CwUKIhyq6abdu3cT+/furPtpKYtq3j4uT7sd38mSE1UraDz+g6HS4tm2L55Ah+D30IIpeT8Yvv3B5zr/R+vrS5K23cO/Zo9bGVJTU7xaQ+MYbNPt5hVx0lUgklUZRlANCiG5VuabCmbwQolI17BRF+QJYXZWb1xbG7t3xuXcCqd9+C4Dv5MkETp+Goi/uvvEeOxZ9i5bEPv0PLk2eTNDsF/GdOLFWxyaEIH3pUgyRkVLgJRJJrVMjd42iKMFCiMuOp+OBYzUfknMIeuEFNEYjuvBwGt15Z+lFtwG3yA60WLOG2CefJPmDD/EaMQIXf/9aG1dBLdfGL/271u4hkUgkBdR04fVtRVGOKopyBBgE/NMJY3IKiosLgTNm4HPXXWUKfAEaNzeCXnwRkZvL5dlzqMiFVRPSlxbUch1da/eQSCSSAmok8kKISUKIG4QQHYUQtxaZ1dc7XJs3J3D6NLI3bSL9p58qbG9LTsYSdbpK97CbTGSuXo3XiBGylqtEIvlLkFmxiuAzaRLZW7aS8NLLmPYfIGDqM2C3k7lmDaZ9+8jPyFRdOToXsrdsBSEI+2Q+Hv36Var/7B07sOfk4D1+XC1bIpFIJCpS5IugaDSE/Oc/pHzxBanffkvmr7+C3Q6KgmubNrj4+2NNSsKenY3XyJHk7NxJ/L9m0mLtr2i9vSvsP2fHDjRGI8YbZS1XiUTy1yBF/hq0Hu4E/nMqPvdOIO3HH9F6eeM1ejS6oMASbS0nTnD+9juIn/k8IfPeQeNefjrinB07MfbqhaIrWalKIpFIaoMGt+PVWeiCggicOhW/hx4sVeABDO3bE/TCC2Rv2cKFCfdiPn4ca1xcqW1z9u7FGhODx8ABtTlsiUQiKYYU+RriO+k+wj7/HGtCAhduv4Ozo0ZjOniwRLuUTz9D6++P9623XodRSiSSvytS5J2AR98+NFu6BL/Hp6BxcyP+2ecQeXmF5/MuXSJn505875uIxlA7ueklEomkNKTIOwl9eDiBzzxDk3fexhobS/Inn2DPyQEgfclSUBS8x8moGolE8tciF16djHvfvniOHEnKJ5+S8smnaL29yc/IwHP4cHSNS0sBJJFIJLWHFHknoygKIe+/R/a4seRGR2ONj0fr7Y3/449f76FJJJK/IVLkawFFUfAcOBDPgQOv91AkEsnfHOmTl0gkkgaMFHmJRCJpwEiRl0gkkgaMFHmJRCJpwEiRl0gkkgaMFHmJRCJpwEiRl0gkkgaMFHmJRCJpwCi1Wc+0zJsqSjJwsZqX+wNXnDic60VDsQOkLXWZhmJPQ7EDamZLUyFEQFUuuC4iXxMURdkvhOh2vcdRUxqKHSBtqcs0FHsaih3w19si3TUSiUTSgJEiL5FIJA2Y+ijyn1/vATiJhmIHSFvqMg3FnoZiB/zFttQ7n7xEIpFIKk99nMlLJBKJpJJIkZdIJJKGjBCiVn+AMGATcAI4DjzjOO4LbACiHb99HMcnAkeAo8BOoFORvkYCUcAZYGY595zs6DcamFzk+OtADJBdz+1YBxx2jONTQFuPbdnsuP6Q4yewPtoCeBax4RBqHPQH9fz/7G5H38eBufXAjnVAOrD6muNPOa4VgP91/pt8BSQBxyq4Z6k2V8eWKhlbnR8gGOha5I1wGmgPvF0weGBmwT8RcFORF+tmYI/jsRY4CzQH9Kgi176U+/kC5xy/fRyPC/rr5RhPdUS+Ltnh5fitAMuAe+qxLZuBbg3h/+uadgeA/vXVHsAPuAQEONp9Cwypq3Y42g4BxlBS5LsAEcAFqifyTrHF8bw/0JVyRL48m6tjS62LfCkG/AIMQ/2UCi7yIkaV0tYHiHM87g38VuTc88DzpVwzAfisyPPPgAnXtKmyyNdRO3TAKuDu+moLNRT5umRLkWOtUb8xKvXVHqA7sLHI8UnA/LpqR5HzA7lG5Iucu0A1RN5ZthQ5FkH5Il+hzVWx5S/1ySuKEoH6SbQHCBJCXHacSgCCSrnkYWCt43EI6hungFjHsWupbLtqUxfsUBTlN9SvfVnA0qraUKSfCK7/3+RrRVEOKYoyW1EUpao2FFBHbAG4B/hJON6N1eU623MGaKMoSoSiKC7AOFS3RV214y+hhrZUFqfa/JcV8lYUxQPVtTBVCJFZ9L0shBCKoohr2g9CfYH6/lVjrAx1xQ4hxAhFUQzAQmAwqk+wStQRWyYKIeIURfF0jGUS8F1VO6kjthRwD6od1eZ62yOESFMU5XHgJ8CO6ltuUdV+rrcdzqS+2vKXzOQVRdGhvjgLhRDLHYcTFUUJdpwPRp2VFrTvCHwJjBVCpDgOx1F8JhEKxCmK0tMxCzykKMqtZbVriHYIISyoXx3H1ldbhBAFv7OAH4Ae9dUWR9+dABchxIGq2lHX7BFCrBJC9BRC9EZ1TZyuw3bUKk6ypay+w4rYMgVna1hN/VOV8F8pqDOzD645/g7FFy3edjwOR/2qeNM17V1QF4WacXUxokMp9/MFzqP6wnwcj32vaVOdhdc6YQfgwVU/oAvqTOupemqLCw6/Iur6wlJgSn20pcj5t4CXG8L7BUekk+P4IaB1XbWjSPuBONkn7yxbilwXQfk++Qptroot1fpHrOIL1Bc13OcIV8PLRqGu3m9EDT/6vcg/1pdAWpG2+4v0NQp1NnEWmFXOPR9yvMhngAeLHH8b1b9ld/x+qb7Zger32+cYxzHgv6gzx3r3NwHcUaNQCsL0PqTq4aB1wpYi584BbRvI++VH1LDBE1Q9gut62LENSAbMqO/vEY7jTzue24B44MvraMuPwGXA6hjTw2Xcs1Sbq2OLTGsgkUgkDRi541UikUgaMFLkJRKJpAEjRV4ikUgaMFLkJRKJpAEjRV4ikUgaMFLkJRKJpAEjRV4ikUgaMP8PL8lD2MB+4m4AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "r = s.rolling('30D')\n",
    "plt.plot(s)\n",
    "plt.title('BOLL LINES')\n",
    "plt.plot(r.mean())#日均线\n",
    "plt.plot(r.mean()+r.std()*2)#日均线+两倍N日标准差线\n",
    "plt.plot(r.mean()-r.std()*2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "对于`shift`函数而言，作用在`datetime64`为索引的序列上时，可以指定`freq`单位进行滑动："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-02-20   -1\n",
       "2020-02-21   -2\n",
       "2020-02-22   -1\n",
       "2020-02-25   -1\n",
       "2020-02-26   -2\n",
       "dtype: int64"
      ]
     },
     "execution_count": 73,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.shift(freq='50D').head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "另外，`datetime64[ns]`的序列进行`diff`后就能够得到`timedelta64[ns]`的序列，这能够使用户方便地观察有序时间序列的间隔："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0   2020-01-01\n",
       "1   2020-01-02\n",
       "2   2020-01-03\n",
       "3   2020-01-06\n",
       "4   2020-01-07\n",
       "dtype: datetime64[ns]"
      ]
     },
     "execution_count": 75,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_series = pd.Series(s.index)\n",
    "my_series.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0      NaT\n",
       "1   1 days\n",
       "2   1 days\n",
       "3   3 days\n",
       "4   1 days\n",
       "dtype: timedelta64[ns]"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "my_series.diff(1).head()\r\n",
    "#逐个相减：1,1,3,1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 2. 重采样\n",
    "\n",
    "重采样对象`resample`和第四章中分组对象`groupby`的用法类似，前者是针对时间序列的分组计算而设计的分组对象。\n",
    "\n",
    "例如，对上面的序列计算每10天的均值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01   -2.000000\n",
       "2020-01-11   -3.166667\n",
       "2020-01-21   -3.625000\n",
       "2020-01-31   -4.000000\n",
       "2020-02-10   -0.375000\n",
       "Freq: 10D, dtype: float64"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.resample('10D').mean().head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "同时，如果没有内置定义的处理函数，可以通过`apply`方法自定义："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01    3\n",
       "2020-01-11    4\n",
       "2020-01-21    4\n",
       "2020-01-31    2\n",
       "2020-02-10    4\n",
       "Freq: 10D, dtype: int32"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.resample('10D').apply(lambda x:x.max()-x.min()).head() # 极差"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "在`resample`中要特别注意组边界值的处理情况，默认情况下起始值的计算方法是从最小值时间戳对应日期的午夜`00:00:00`开始增加`freq`，直到不超过该最小时间戳的最大时间戳，由此对应的时间戳为起始值，然后每次累加`freq`参数作为分割结点进行分组，区间情况为左闭右开。下面构造一个不均匀的例子："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01 08:26:35   -1\n",
       "2020-01-01 08:27:52   -1\n",
       "2020-01-01 08:29:09   -2\n",
       "2020-01-01 08:30:26   -3\n",
       "2020-01-01 08:31:43   -4\n",
       "Freq: 77S, dtype: int64"
      ]
     },
     "execution_count": 77,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "idx = pd.date_range('20200101 8:26:35', '20200101 9:31:58', freq='77s')\n",
    "data = np.random.randint(-1,2,len(idx)).cumsum()\n",
    "s = pd.Series(data,index=idx)\n",
    "s.head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "下面对应的第一个组起始值为`08:24:00`，其是从当天0点增加72个`freq=7 min`得到的，如果再增加一个`freq`则超出了序列的最小时间戳`08:26:35`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01 08:24:00   -1.750000\n",
       "2020-01-01 08:31:00   -2.600000\n",
       "2020-01-01 08:38:00   -2.166667\n",
       "2020-01-01 08:45:00    0.200000\n",
       "2020-01-01 08:52:00    2.833333\n",
       "Freq: 7T, dtype: float64"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.resample('7min').mean().head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "有时候，用户希望从序列的最小时间戳开始依次增加`freq`进行分组，此时可以指定`origin`参数为`start`："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 78,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01 08:26:35   -2.333333\n",
       "2020-01-01 08:33:35   -2.400000\n",
       "2020-01-01 08:40:35   -1.333333\n",
       "2020-01-01 08:47:35    1.200000\n",
       "2020-01-01 08:54:35    3.166667\n",
       "Freq: 7T, dtype: float64"
      ]
     },
     "execution_count": 78,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.resample('7min', origin='start').mean().head()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "在返回值中，要注意索引一般是取组的第一个时间戳，但`M, A, Q, BM, BA, BQ, W`这七个是取对应区间的最后一个时间戳："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-31    0.451613\n",
       "2020-02-29    0.448276\n",
       "2020-03-31    0.516129\n",
       "2020-04-30    0.566667\n",
       "2020-05-31    0.451613\n",
       "Freq: M, dtype: float64"
      ]
     },
     "execution_count": 80,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = pd.Series(np.random.randint(2,size=366), index=pd.date_range('2020-01-01', '2020-12-31'))\n",
    "s.resample('M').mean().head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2020-01-01    0.451613\n",
       "2020-02-01    0.448276\n",
       "2020-03-01    0.516129\n",
       "2020-04-01    0.566667\n",
       "2020-05-01    0.451613\n",
       "Freq: MS, dtype: float64"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s.resample('MS').mean().head() # 结果一样，但索引不同"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 六、练习\n",
    "### Ex1：太阳辐射数据集\n",
    "\n",
    "现有一份关于太阳辐射的数据集："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Data</th>\n",
       "      <th>Time</th>\n",
       "      <th>Radiation</th>\n",
       "      <th>Temperature</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>9/29/2016 12:00:00 AM</td>\n",
       "      <td>23:55:26</td>\n",
       "      <td>1.21</td>\n",
       "      <td>48</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>9/29/2016 12:00:00 AM</td>\n",
       "      <td>23:50:23</td>\n",
       "      <td>1.21</td>\n",
       "      <td>48</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>9/29/2016 12:00:00 AM</td>\n",
       "      <td>23:45:26</td>\n",
       "      <td>1.23</td>\n",
       "      <td>48</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                    Data      Time  Radiation  Temperature\n",
       "0  9/29/2016 12:00:00 AM  23:55:26       1.21           48\n",
       "1  9/29/2016 12:00:00 AM  23:50:23       1.21           48\n",
       "2  9/29/2016 12:00:00 AM  23:45:26       1.23           48"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df = pd.read_csv('data/solar.csv', usecols=['Data','Time','Radiation','Temperature'])\n",
    "df.head(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "1. 将`Datetime, Time`合并为一个时间列`Datetime`，同时把它作为索引后排序。\n",
    "2. 每条记录时间的间隔显然并不一致，请解决如下问题：\n",
    "* 找出间隔时间的前三个最大值所对应的三组时间戳。\n",
    "* 是否存在一个大致的范围，使得绝大多数的间隔时间都落在这个区间中？如果存在，请对此范围内的样本间隔秒数画出柱状图，设置`bins=50`。\n",
    "3. 求如下指标对应的`Series`：\n",
    "* 温度与辐射量的6小时滑动相关系数\n",
    "* 以三点、九点、十五点、二十一点为分割，该观测所在时间区间的温度均值序列\n",
    "* 每个观测6小时前的辐射量（一般而言不会恰好取到，此时取最近时间戳对应的辐射量）\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#将Datetime, Time合并为一个时间列Datetime，同时把它作为索引后排序。\r\n",
    "#df['Data'] = df['data']\r\n",
    "solar_date = df.Data.str.extract('([/|\\w]+\\s).+')[0] \r\n",
    "df['Data'] = pd.to_datetime(solar_date + df.Time)\r\n",
    "df\r\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 2.0.0b0 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
